2017年1月9日月曜日

STEP UP Haskell!! -2-

mainpicture

先日から引き続きhaskellを使っていこう。

yesodというwebのフレームワークを利用しながら開発をしていくつもりなので、

この二つのアプローチで学習していく


STEP UP Haskell!! -2-

  1. yesod環境の構築
  2. helloworld.hs
    1. sample1 helloworld
    2. sample2 links
    3. sample3 json

yesod環境の構築

まずはyesodでの開発を可能にする。
Yesod quickstartに従えばできるはず。

….

と思っていた時期が私にもありました。

私の場合はstack build yesod-bin cabal-install --install-ghcここで失敗した。。。

理由はGHC panic when building Stack on macOS Sierraというghc7系とMac OS Sierraの相性の問題の模様。

最新版の8.0.2では修正が入っているようだが、まだリリースされていない(2017/01/08時点)
また、8.0.2で仮に動作したとしてもyesodに必要なlib郡が全部入るのかどうかも分からない。
stackageにはまだ8.0.2のyesod libが定義されていないし)

詰んだ。と思ったが要はOSを変えればいいのだ。
なのでwindowsに逃げるなりvmを立ち上げるなりをすればいいのだ。

調べたところによるとhaskell stackがdockerサポートを結構やっているらしく、
optionを一つ追加するだけでstackコマンド全般がdockerのcontainer上で動作するように設定が可能っぽい。

以下のような手順でとりあえずyesodサンプル(db migrateからlocalhost:3000でのアクセスまで)ができるようにした。

docker install
 (これは各OS毎に異なるかも、Macはdocker platformがあったのでdmgからinstallした)

stack docker pull
(stack.ymlに準拠したfpco/stack-buildという名称のimageが取得できる)

stack new my-project yesod-mysql && cd my-project

ここまでやったらstack.ymlが出来上がるので、

docker:
  enable: true

これを追記。

x stack build yesod-bin cabal-install --install-ghc
(マニュアルによるとこれだが、install-ghcだとstackがいれたやつになりそうだったからやめた)

stack build yesod-bin cabal-install
(こっちにした)

stack build

これでstack関係はOK。

# ここからmysqlのinstall
brew install mysql
mysqld --initialize --explicit_defaults_for_timestamp
(passwordメモっとくこと)
mysql_secure_installation
mysqld

これでmysqlは起動する。

で、mysqlの設定をyesodプロジェクトの方に記載する必要があるので、
config/setting.ymlの該当箇所を以下のように編集

database:
  user:     "_env:MYSQL_USER:yourusername"
  password: "_env:MYSQL_PASSWORD:yourpassword"
  host:     "_env:MYSQL_HOST:192.168.11.2" # your host IP address, not hostname
  port:     "_env:MYSQL_PORT:3306"
  # See config/test-settings.yml for an override during tests
  database: "_env:MYSQL_DATABASE:yourdbname"
  poolsize: "_env:MYSQL_POOLSIZE:10"

hostはlocalhostだとsocketを利用しにいくので、必ずIPで指定。
というのもdocker上で動いているcontainerからhostマシンのmysqlに接続にいくので
sockerファイルの共有の設定がめんどくさいからだ。

ここのIPアドレスはhostマシンでifconfigで出てるやつを設定しておけばいい。
公式のドキュメントによればcontainerとhostマシンのネットワークは同様に使えるとのことだった。

最後に

stack --docker-run-args='--net=bridge --publish=3000:3000' exec -- yesod devel

この--docker-run-argsとか不要だと思うのだけど何故かつけないと
yesodのdevel serverにhostマシンのブラウザからアクセスができなかった。

refs: http://qiita.com/tanakh/items/6866d0f570d0547df026

今後の世界を考えてもdocker imageでcontainerを作ったり破棄したりを繰り返すようになるだろうし、docker imageの中に含まれていてもなんら問題はないだろう。
(むしろdeploy時にも楽ができるようになるんじゃないかと目論んでいる)
どちらにせよserveするときにはimage化したいし。

refs: https://docs.haskellstack.org/en/stable/docker_integration/

以降はチュートリアルをやってみる。


helloworld.hs

railsとかと違って全然知識不足により即実装とはなかなか行きづらいのでチュートリアル


sample1 helloworld

{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeFamilies          #-}
import           Yesod

data HelloWorld = HelloWorld

mkYesod "HelloWorld" [parseRoutes|
/ HomeR GET
|]

instance Yesod HelloWorld

getHomeR :: Handler Html
getHomeR = defaultLayout [whamlet|Hello World!|]

main :: IO ()
main = warp 3000 HelloWorld

これをコピペして実行。

解説が載っているので割愛するが、一部分からないのがQuasiQuotesという単語。

準クォートという名称らしいのだが、
http://d.hatena.ne.jp/ruicc/20111015/1318630433
http://haskell.g.hatena.ne.jp/mr_konn/20101210/quasiquotes
http://www.slideshare.net/konn/metaprogramming-in-haskell

この辺りの解説くらいしか目ぼしいものが見つからない。

基礎勉強の方のアプローチでそのうち出てくることを祈ろう。
とっても薄い理解だと、クォート(”)は文字列リテラルを表しているように、
何か別な記号や関数名(?)に意味を持たせて実行可能にする。
(クォートではないが表記としては同じような書き方ができるから準クォート?)

間違いが発覚したり、後日理解が進んだら訂正します。

この準クォートに当たるのがparseRoutes。
名前からするとyesodが用意してくれたpath名のparserか。
HomeR/GETリクエストが来たら答える。というコード。

HomeRのRはresourceの意味らしい。慣習的につけているっぽい

規約により、http methodとresource nameをつなげてhandlerを定義する。
今回はGET + HomeRなのでgetHomeRをhandlerとして定義している。
whamletはQuasiQuotesらしい。(やっぱ微妙にわかりにくい。。。)
このケースだとHamlet Syntax(Hamletはyesodのtemplate engine)からWidgetを作り出すとのこと。

時たま解説にTH functionというのが出てくるがおそらくTemplateHaskell functionの略っぽい。

ddump-splicesこのオプションを入れてghcを実行すると構文木を接合した状態のものを出力してくれる。
上記準クォートの処理結果を必要なところで展開して見せてくれるような機能だと理解した。
stackコマンドを利用しているならstack runghc -- -ddump-splices helloworld.hsで実行可能だ。

あとの細かいことは後述されるとのことだったので、他のチャプターを読んだ時に出てくるのだろう。


{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeFamilies          #-}
import           Yesod

data Links = Links

mkYesod "Links" [parseRoutes|
/ HomeR GET
/page1 Page1R GET
/page2 Page2R GET
|]

instance Yesod Links

getHomeR  = defaultLayout [whamlet|<a href=@{Page1R}>Go to page 1!|]
getPage1R = defaultLayout [whamlet|<a href=@{Page2R}>Go to page 2!|]
getPage2R = defaultLayout [whamlet|<a href=@{HomeR}>Go home!|]

main = warp 3000 Links

Linksアプリケーションが出来上がった。

注目すべきは

  • /page1/page2が出来上がっていること
  • aタグの中にdata constructorであるPage1Rなどが渡されていること

page1やpage2を足したのでhandlerをそれぞれ命名規約通り(getPage1R、getPage2R)を定義する。

Page1Rとかは

renderRoute HomeR  = ([], [])
renderRoute Page1R = (["page1"], [])
renderRoute Page2R = (["page2"], [])

こんな感じで内部的には処理されているらしいので、所詮ただのタプルである(第一引数はpath、第二引数はquery)
これをaタグのリンクにいれられるため、URLの変更に強かったりする。
確かrailsでもActiveRecordのmodelをa_tagに渡すことができたような気がするがそれに近いのかもしれん。


sample3 json

{-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE QuasiQuotes          #-}
{-# LANGUAGE TemplateHaskell      #-}
{-# LANGUAGE TypeFamilies         #-}
import Yesod

data App = App

mkYesod "App" [parseRoutes|
/ HomeR GET
|]

instance Yesod App

getHomeR  = return $ object ["msg" .= "Hello World"]

main = warp 3000 App

これでjson responseが出来上がる。

{ msg: "Hello World" }

これはどの言語で書いてもこんなもんか。

今日はこんなところで。

では良いインプットと良いプログラミングを。

2017年1月6日金曜日

STEP UP Haskell!! -1-

mainpicture

新しいサービス開発を行うことになった。

rubyやjsでやれば2週間で実現できそうな内容だったが、
3ヶ月程度期間があるのでhaskellでやってみることにした。

私にそこまでhaskellに関しての知見があるわけではない。 本を一冊読んだ程度だ。

そこで、ある程度の基礎やキャッチアップした内容をメモを残していく。


STEP UP Haskell!! -1-

  1. Haskellとは何者なのか?

Haskellとは何者なのか?


関数を第一級関数として取り扱うことのできる言語である。
静的型付けの言語である。

これらの性質により副作用がなく参照透過性のよいプログラムを書くことができる。

他の言語にも関数を渡すことのできるものはある。
javascriptやらpythonなどは比較的容易にそれを可能とする。

違いをあげるとすれば静的な型付けが行われるという点だ。

これにより渡された関数は何をinputとして受け取り何をoutputとして返すのかを理解することができる。

Haskellとはこの関数自体に型を定義することが可能である。
(javaやC++のように値に型を定義できる言語のように)

Middleware: Application => Application
Application: Request => Response

といったようなことが可能なのであろう。

ちなみに別な書籍ではすべての元はラムダ計算(lambda calculus)であるという記載もあった。

こちらも気になったので少し調べてみた。


ラムダ計算(lambda calculus)

以下の二つの特徴からなるラムダ式を構築してそれらを連結していくものとして捕らえよ。みたいな記載だったが少しわかりにくい。

  1. アルファ変換
    ラムダ計算の内で、束縛変数(binding variable)と自由変数(free variable)が被らなければ
    変数の名前(a,b,c, x,y,z)などは重要ではない。
    その規則の中で束縛変数(binding variable)を置き換えてもいいよ。
    という規則。

    λa.a
    λx.x

    は同義だよ。
    とのこと。

  2. ベータ変換
    ラムダ計算で、λを一つ一つ関数を適用していくこと。

    a.a) 1

    これはλにa := 1を適用することにより

    1

    を得ること。


少し頭がヒートしてきたので今日はここまで。

では良いインプットと良いプログラミングを。

2016年4月13日水曜日

ECMAScript2015 (ES6) のまとめ / Promise, Set, Map

mainpicture

さて、しばらく間があいてしまいました。ES6の最終話です。

前回までの話で大きなところは大体抑えられたかと思います。今回は、いままで外部のライブラリ等に頼っていた部分がES6にて標準実装になったモノがあるのでその辺りをまとめていきます。

より詳細を確認したい方はECMAScript2015の公式を確認すると良いかと。


ECMAScript2015のまとめ

ES5と比較した時のES6の特徴としては以下のものが挙げられます。(言語仕様を読むと他にもいろいろと定義はされています)

  • let
  • template string
  • arrow function ( => )
  • default parameter ( function(x, y=10) { )
  • spread operator ( ...[1,2,3] )
  • rest parameter ( function(x, y, ...z) { )
  • class
  • import, export
  • Promise
  • Set, Map(今日はここまで)

今回はPromise, Set, Mapを説明していきます!


Promise

webサービスを開発したことがある方なら触ったことがあるかもしれません。非同期処理を書いていく際にcallbackでネストが深くなってしまうような箇所をthen関数やcatch関数を利用することにより、浅く見通しよく書けるようになります。

angularJSではqというライブラリが利用されていたと記憶しています。

まずはES5にてネストがいくつか連なっている例を挙げると、

// ES5 ネスト
var func1 = function(cb) {
  console.log('in func1');
  cb();
}

var func2 = function(cb) {
  console.log('in func2');
  cb();
}

var func3 = function(cb) {
  console.log('in func3');
  cb();
}

func1(function() {
  func2(function() {
    func3(function() {
      console.log('last call');
    });
  });
});

このような形になります。私は別にこれで書いても全く悪くないとは思っていますし、azureのnodejsのsdkの内部にもこういった内容が結構あります。ただ、例外処理などが入ってくると徐々に書きにくくなっていったりするのも事実です。

callback形式で例外処理をするさいには、callbackの第一引数にErrorを詰めて返すのが割とよく見られるやり方です。

func1(function(err) {
});

このような形になります。他のやり方として、第二引数に例外時の関数を渡したりという設計も可能ですが、個人的にはかなり読みにくくなるのであまり好きではなありません。

func1(function() {
// 正常処理
}, function(err) {
// 例外処理
});

さてこれをES6のpromiseを使って書き換えてみます。

// ES6 promise
var func1 = function(cb) {
  return new Promise(function(resolve) {
    console.log('in func1');
    resolve();
  });
}

var func2 = function() {
  console.log('in func2');
}

var func3 = function() {
  console.log('in func3');
}

func1().
then(func2).
then(func3).
then(function() {
  console.log('last call');
});

promiseを使うことにより、func2とfunc3がcallbackを呼び出すという役割は不要になりました。シンプルにその関数で必要な処理を記載するだけです。

値を次のfunctionへ渡していきたい場合にはreturnをすれば次の関数で受け取ることができます。

var func2 = function() {
  console.log('in func2');
  return 'from func2'
}
var func3 = function(message) {
  console.log('in func3');
  console.log(message);
}

また、エラー処理も読みやすく書くことができます。

// ES6 promise
var func1 = function(cb) {
  return new Promise(function(resolve) {
    console.log('in func1');
    resolve();
  });
}

var func2 = function() {
  console.log('in func2');
  throw new Error('oh no');
}

var func3 = function() {
  console.log('in func3');
}

func1(). // in func1
then(func2). // in func2
then(func3). // ここは実行されない
then(function() { // ここも実行されない
  console.log('last call');
}).catch(function(e) {
  console.log(e); // [Error: oh no]
});

catch関数を呼ぶことにより、読みやすい例外処理を行うことが可能です。

実はnew Promiseの引数に渡している関数の第二引数にrejectを渡すことも可能です。rejectを利用するとfunc1から明示的にcatch関数まで飛ばすことができます。

var func1 = function(cb) {
  return new Promise(function(resolve, reject) {
    console.log('in func1');
    if (true) {
      reject();
    }
    resolve();
  });
}

ただし注意すべき点があります。それは、ここでrejectした場合、thenの第二引数で処理を受けることも可能であるということです。

func1().
then(func2, func3).
then(func3).
then(function() {
  console.log('last call');
}).catch(function(e) {
  console.log(e);
});

このような形になっている場合、func1の後の処理は2行目のfunc3に渡ることになります。

Promiseに関してはこれくらいです。慣れるまで少し時間がかかりますが、比較的読みやすくなるので個人的には好きです。構文がrubyのblockに少し似ていて(chainはできないけど)そこまで読みにくさは感じませんでした。


Set, Map

二つまとめていきましょう。SetとMapについてです。

まずはSet。

Setは重複しない値を保持するためのデータ構造になります。

var s = new Set([1, 2, 3, 'aaa', 'bbb']);
console.log(s);
console.log(s.entries());
s.add(1)
s.add('ccc')
console.log(s);
var s = new Set([1,1,1,1]); // Set[1]
console.log(s);

これで大体概要はつかめるのではないでしょうか。重複した値を保持しないようにできているため、大量データをfilterしながらループ処理するときなどSetとaddを使えば重複する可能性などを考えずシンプルにコーディングすることができます。(arrayのfilter使えよという話もありますがw)

ちなみに値が同一であるというのはECMAScript6 7.2.10SameValueZeroで判定されているようです。興味のある方は目を通してみると良いかと思います。

次はMap。

オブジェクトと少し似ているのですが、keyに関数を指定したり、getやsetやkeyの取得などの関数が用意されています。

var data = new Map([
    ['a', 'aaa'],
    ['b', 'bbb'],
]);

console.log(data);
console.log(data.get('a'));
console.log(data.size);

for (var k of data.keys()) { // Iterator
  console.log(k);
}

と例を出してみたものの、個人的にはオブジェクトで十分であるような気もしています。そこまでこの構造がないと困ったというような局面になったことがないので、いまのところ有用性があまりわかっていないです。keyを取得するのが簡単になったのはかなり嬉しいですが。(これまではObject.keys(data)とかやってた)

最後の最後に肩透かしで申し訳ない。


さて、これにてECMAScript6のメインとなりそうなトピックの説明が完了しました。いろいろな言語がありますが、Chrome or node(V8), Firefox(Gecko?最近変わった??), Safari(webkit)などメジャーな実行環境・実装がいくつもある言語もそうないのではないかと思っています。しかも開発する際にはある程度気を配る必要があるという…。そんな中で標準というものが出来上がってきつつあり、プログラマとしては非常にありがたいです。

jsに関してはこれからも出来ることが増えていくことに期待しています。言語的には非常に柔軟で、スモールプロダクトであればかなり早く、かつ、クライアントもサーバーも同じ言語で記述できるという良い特性を備えつつあると思い活用しています。

次は今作っているwebサービスの話なんかができれば良いかな。

では、良いインプットと良いプログラミングを。

2016年3月28日月曜日

ECMAScript2015 (ES6) のまとめ / class, import, export

mainpicture

あと2回でES6に関しては終了です。次回はdockerかpythonによる機会学習あたりのネタを書こうかな。haskellでもいいな。IoTネタも少し書きたい。

前々回はES6に追加されたシンタックスシュガーや変数宣言の方法、前回はパラメータの渡し方や展開の新しい方法についてまとめました。

今回は、いよいよES6のメインかもしれないClassとモジュールについて取り上げます。

より詳細を確認したい方はECMAScript2015の公式を確認すると良いかと。


ECMAScript2015のまとめ

ES5と比較した時のES6の特徴としては以下のものが挙げられます。(言語仕様を読むと他にもいろいろと定義はされています)

  • let
  • template string
  • arrow function ( => )
  • default parameter ( function(x, y=10) { )
  • spread operator ( ...[1,2,3] )
  • rest parameter ( function(x, y, ...z) { )
  • class
  • import, export(今日はここまで)
  • Promise
  • Set, Map

今回はES6におけるclassとそれらを含むmodule importやexportをまとめます。


class

ES5以前でも同様のclassのようなものを作ることはできました。しかし、イマイチ読みづらく書きづらい。prototypeの理解をしないと少しイメージがしずらかったり、スコープを限定して書くこともやりにくく野良化しやすいです。

実例を出した方が理解が早いですね。まずはES5 ver。

// ES5

var User = function(name, mail) {
  this.name = name;
  this.mail = mail;
};

User.prototype.getName = function() {
  return this.name
}

var user1 = new User('Test User1', 'test1@test');
var user2 = new User('Test User2', 'test2@test');
console.log(user1.getName()); // Test User1
console.log(user2.getName()); // Test User2

// 後から宣言も可能
User.prototype.getMail = function() {
  return this.mail
}

console.log(user1.getMail()); // test1@test
console.log(user2.getMail()); // test2@test

// overrideも出来る
user1.getName = function() {
  return "<<" + this.name + ">>";
}
console.log(user1.getName()); // <<Test User1>>
console.log(user2.getName()); // Test User2

このように割と自由度高くどこでも再定義が可能で、prototypeを使ってmethodを定義するという形になります。(prototypeに関しては他の記述に任せますが、すべてのFunctionObjectの雛形のようなもので、newで生成したinstanceのクラスのbaseになるものです。)

さて上記例をどうようにES6で書き換えると次のようになります。

// ES6

class User {
  constructor(name, mail) {
    this.name = name;
    this.mail = mail;
  }

  getName() {
    return this.name;
  }
}

var user1 = new User('Test User1', 'test1@test');
var user2 = new User('Test User2', 'test2@test');

console.log(user1.getName());
console.log(user2.getName());

// これももちろんできるが、クラスに書くべし
User.prototype.getMail = function() {
  return this.mail
}

console.log(user1.getMail());
console.log(user2.getMail());

user1.getName = function() {
  return "<<" + this.name + ">>";
}
console.log(user1.getName());
console.log(user2.getName());

これで他の言語と同様にclassを定義することができるようになりました。

とはいえそれでも自由度の高い言語であることは変わらず、ダッグタイピングも可能ですし、そこでどんな定義をしても変数を突っ込んでも動かそうと思えば動かすことができます。

個人的にはthisあたりも冗長であることや、javascriptのthisは少し混乱を招きやすいのでthisは書きたくないなと感じています。


import, export

さて、Classを使えばある程度まとまった形でプログラムを書くことができるようになります。しかし、javascriptは依存関係の解決がとても難しい言語でした。htmlのhead内とかに記載しているため、どこでどの関数、変数が定義されているのか、参照しているソースコードはどれなのかとても追いにくく、関数名のバッティング等にも気をつける必要がありました。

しかし、ES6には標準でmodule化、importやexportの仕組みが用意され、お互いの依存関係を容易に示すことが可能になりました。

nodejsではrequireとmodule.exportで記載されていますが、ほぼ同様のことができるようになっています。

さきほどのUserクラスの簡易版をmodule化し、他のコードから読み込む場合は以下のようになります。

// lib.js

'use strict';

class User {
  constructor(name, mail) {
    this.name = name;
    this.mail = mail;
  }
  getName() {
    return this.name;
  }
}

export default User;
// main.js
'use strict'

import User from './lib';

var u = new User('hogehoge', 'hogehoge@hoge');
console.log(u.getName());

このような形になります。

自作のlibを参照する際には./で始めると相対パスで依存関係を解決しにいきます。npmでインストールしたlibなどはimport _ from 'lodash'import async from 'async'などのようにそのままimportすれば利用可能になります。

nodeの場合はimport、exportの記法が多少異なる( requireとかになる)のでご注意ください。あくまでもES6の標準です。

これで依存関係の把握が容易になり、global汚染もかなり解決されるようになりました。


私はできるだけ短く、できるだけ簡単にソースが書けるように頑張っております。すっきり書けるととても面白いものです。

さて、次回はES6に関しては最後になります。取り上げるのはES6で組み込まれたpromiseとset, mapに関してです。promiseは少し独特な動きをするので慣れるまで大変かもしれませんが、結構便利なものです。読んでいただければと思います。

では良いインプットと良いプログラミングを。

2016年3月22日火曜日

ECMAScript2015 (ES6) のまとめ / default parameter, spred operator, rest parameter

mainpicture

前回に引き続き、ES6の変更点を見ていきます。

より詳細を確認したい方はECMAScript2015の公式を確認すると良いかと。


ECMAScript2015のまとめ

ES5と比較した時のES6の特徴としては以下のものが挙げられます。(言語仕様を読むと他にもいろいろと定義はされています)

  • let
  • template string
  • arrow function ( => )
  • default parameter ( function(x, y=10) { )
  • spread operator ( ...[1,2,3] )
  • rest parameter ( function(x, y, ...z) { )(今日はここまで)
  • class
  • import, export
  • Promise
  • Set, Map

前回は新しいarrow functionや変数宣言の新しいスコープであるletなどを取り上げました。

今回は、関数へ渡す引数やパラメータ展開の話がメインになります。


default parameter

rubyやpythonに見られるデフォルト引数という仕様です。引数に値が渡されなかった場合にこちらの値を利用することになります。メソッドのオーバーロードを書かなくて済むという利点がありますが、実際にどんな引数が渡ってきているのかを把握しておかないと何故か動いてしまうというソースを書いてしまうことになりかねないので注意が必要です。

ES5で実現するためには関数内で引数を取得するargumentsと呼ばれるオブジェクトを利用したり、関数名を変えたりしていることかと思います。

// ES5
var checkarg = function(i1,i2) {
  console.log(i1) # 1
  console.log(i2) # 2
  console.log(arguments['2'] || 'def') # def
}
checkarg(1,2)

これを次のように書き換えることができるようになりました。

// ES5
var checkarg = function(i1,i2,i3='def') {
  console.log(i1) # 1
  console.log(i2) # 2
  console.log(i3) # def
}
checkarg(1,2)

Firefoxには実装済みのようですが、chromeとnodeでは動作が確認できませんでした。

このデフォルト引数は、スクリプト言語では割と有用だと個人的には思っています。確かにメソッド内部で値の存在判定を行い、デフォルト値っぽいのを使うことができるのですが、多少煩雑ですし何を意図しているのか分かり難い感じになってしまうため、これを使えるとソースコードが読みやすくなりますね。


spread operator

私がこの演算子に出会ったのはreact-nativeを使ってアプリを実装しているときでした。react-nativeのNavigatorSceneConfigsのtransitionで使われています。

var FadeToTheRight = {
  ...FadeToTheLeft,
  transformTranslate: {
    from: {x: 0, y: 0, z: 0},
    to: {x: Math.round(SCREEN_WIDTH * 0.3), y: 0, z: 0},
  },
  translateX: {
    from: 0,
    to: Math.round(SCREEN_WIDTH * 0.3),
  }
};

・v0.15だったので今は違うかもしれないですが。
・上の例はES7のproposalで、babelがpluginを用いて利用可能にしています。純粋なES6の実装では使うことができません。

...FaceToTheLeftというのが中にあるのが確認できます。FadeTransactionの基本的な設定のようなものが、FaceToTheLeftに定義されていて(遷移の最後のほうでopacityが減るなど)、FadeToTheRightで一部変更がある部分を定義していることになります。

もう少し簡単なspread operatorの例を挙げてみましょう。

// ES5
var array1 = [1,2,3]
console.log(array1);
var array2 = [4,5,6]
console.log(array2);

console.log(array1.concat(array2)); 

最後のarray1の連結部分が

// ES6
var array1 = [1,2,3]
console.log(array1); // [1,2,3]
var array2 = [4,5,6]
console.log(array2); // [4,5,6]

console.log([...array1, ...array2]) // [1,2,3,4,5,6]

と、こんな風に書くことができます。

かなり見通しよく書けるようになりました。割とこの辺りはlodashとかを利用して 書くことが多いのですが、標準仕様で使えるようになるとありがたいですね。


rest parameter

最後はrest parameterについてです。他の言語だと可変長引数で同様のことができるようになっていたりします。

むしろjavascript今までできなかったのかよ的な雰囲気すらあります。おそらくこれまではargmentsでどうにかしていたのでしょう。

こんな感じになります。

//ES6
function expose(a1, ...rest) {
  console.log(a1) // 1
  console.log(rest) // [2,3]
}
expose(1,2,3);

node5系で動作させる場合には、コマンドに「–harmony_rest_parameters」というオプションを入れる必要があります。
node --harmony_rest_parameters hogehoge.js

残りのパラメーターが配列で入ってくるのが楽ですね。長さを気にせず渡すことができます。
(あまり長すぎるのも可読性が落ちるので気をつけましょう。)


今回は引数に関して主にまとめてみました。個人的にはbabelで利用出来るspread operatorのhash版が結構使えるやつなのでオススメです。

特にreateを実装しているとstateを書き換える部分があるのですが、そこを読みやすくかけるので。

this.setState({
  ...this.state,
  field: newValue,
});

さて、次回からES6のクラスとモジュール周りをまとめていきます。

では良いインプットと良いプログラミングを。

2016年3月14日月曜日

ECMAScript2015 (ES6) のまとめ / let, template string, arrow function

mainpicture

これをお読みの方は、どんな言語を用いて開発を行っているでしょうか。

javascriptはひと昔前までは主にブラウザ上で動きをつけるために利用され、私の感覚からするとその頃はとても書きにくい理解しづらい言語でした。(自分のプログラムの能力のせいもありますがw)しかし、nodejsが流行り始めた5,6年くらい前くらいからjavascriptはかなり勢いを増し、現在でもその勢いは大きく落ちることなく、結果多くの方が利用するようになっているのではないかなと思います。

最近ではnodejsも多くのPaaSの上で動作することが可能となり、バックエンドがjavascriptで記述することができるようになったため、フロントエンドとバックエンドで使用する言語を同一にするという目的のためにjavascriptを選択するのも良い選択になりました。

さらに、フロントエンドの開発が進んでいくうちにAngularJSreactといった数々のフレームワークが生まれ、SinglePageApplicationという考え方が生まれました。これらのフレームワークは今でも積極的にアップデートが重ねられ、リッチなWebアプリケーションを構築する際に大きく役に立ちます。

しかし、通常のjavascriptを利用して開発を行うと、言語仕様のために記述がしにくい箇所がでたり、冗長になったり、ソースコード量が多くなってしまうことがありました。それらを補うためにcoffeescripttypescriptなどが開発されるようになりました。

しかし昨年ECMAScript2015が制定され、ChromeやSafariなどの多くのブラウザによる実装が進んでいるため、coffeescripttypescriptで行われていた多くの部分を標準仕様で解決することができるようになりました。一部実装していないブラウザに関してはトランスパイラと呼ばれるES6をES5に変換するBabelのようなものもあるため、ES6は積極的に利用すべきであると思います。

そこで今回はECMAScript2015を数回に分けて紹介しつつまとめていきます。


ECMAScript2015のまとめ

ES5と比較した時のES6の特徴としては以下のものが挙げられます。(言語仕様を読むと他にもいろいろと定義はされています)

  • let
  • template string
  • arrow function ( => ) (今日はここまで)
  • default parameter ( function(x, y=10) { )
  • spread operator ( ...[1,2,3] )
  • rest parameter ( function(x, y, ...z) { )
  • class
  • import, export
  • Promise
  • Set, Map

さて、少しずつみていきましょう。


let

ES5では変数宣言の際には

// ES5
var s = "I'm string"
var i = 123

このような形で定義していましたが、スコープが大きくなってしまうという問題がありました。

// ES5
function m() {
  var s1 = "s1";
  console.log(s1); // s1
  if (true) {
    var s1 = "changed";
    console.log(s1); // changed
  }
  console.log(s1); // changed
}
m();

これをlet宣言を使うことによりスコープをレキシカルブロック({}で囲まれた範囲)に宣言することができるようになりました。

// ES6
function m() {
  let s1 = "s1";
  console.log(s1); // s1
  if (true) {
    let s1 = "changed";
    console.log(s1); // changed
  }
  console.log(s1); // s1 ←変わらない!
}
m();

これでかなりスコープを限定して記述することが可能になりました。

swiftだとletは再代入不可ですが、ES6のletは再代入が可能です。
ES6の定数はconstで宣言します。


template string

これは他の言語にも良くある形だと思います。文字列フォーマットとも呼ばれているかもしれません。(厳密には文字列フォーマットは数値・文字の判別や数値の桁数などができるので違うかもですが)

// ES6
let pc = "Mac";
console.log(`I'm using ${pc}.`); // I'm using Mac.

こんな形で文字表現がかなり読みやすくなりました。これまでは+で連結しまくっていたので非常に楽です。


arrow function

むしろ今までなくて良くやってこれたなという雰囲気すら感じますが、関数宣言のシンタックスシュガーといえば良いでしょうか。

他の言語だとjava8から実装されたため同様の記法がjava8から使うことができ、Swiftにも同じような構文があります。RubyのProcオブジェクトやブロックとも似ています。

javascriptという性質上サーバーサイドとの連携が多く、遅延が発生するためそういった箇所はcallbackの嵐となるため、それをシンプルに書けるようにするためにはこのarrow functionが欲しくなってきます。

さて、どういったものなのかというと

// ES6
let f = () => {
  console.log("f is called");
}
f(); # f is called

これですね。少しややこしいケースも書いてみましょう。

// ES6
function post(data, callback) {
  setTimeout(() => {
    console.log(`Handle Data: ${data}`);
    callback({isOK: true});
  }, 3000);
}

function postDataToServer(data, callback) {
  post(data, callback);
}

function webapi(data, callback) {
  postDataToServer(data, callback);
}

console.log("Before Post");
webapi("Post Data", (response) => {
  console.log(response.isOK ? "Succeed!!" : "Failed...");
});
console.log("After Post");

このような形で時間のかかる処理(この例だとpost)の処理結果をcallbackが引き受けて、それをarrow functionで定義しておくとソースコードは短くてすみます。

別に読みにくければfunctionで代入も可能です。

// ES5
webapi("Post Data", function(response) {
  console.log(response.isOK ? "Succeed!!" : "Failed...");
});

これでもやっていることは全く変わりません。

ライブラリとかだと、第一に引数にerrorを渡し戻すものも多く存在します。また、この例のようにメソッドが一つくらいであればいいのですが、successとfailで二つのfunctionを定義しなくてはならなくなると徐々にめんどくさくなってきたりします。


さて、いかがでしたでしょうか。

今もまだ昔のjavascriptの書き方をしているのであれば、これを機に少しずつES6の書き方に変えていくのが良いかと思います。
次回は関数へのパラメータ周りを紹介していきます。

では良いインプットと良いプログラミングを。

2016年3月8日火曜日

Go言語とGoogleAppEngineに触れる / TaskQueue, PushQueue, PullQueue

mainpicture

Webサービスの処理に時間のかかる部分がある場合、それをQueueに入れて別プロセスのWorkerに処理をさせたいという部分があります。

例えば、メールを送信する処理であったり、画像の変換だったりですね。

Go言語においては最初はGo-Routineを利用すれば、非同期に処理ができたり、responseを返した後に処理を継続するということができるかなと思っていたのですが、responseが終了した時点でContextが閉じられてしまうせいなのかうまく動作せず、このあたりの方法を探っていたところでした。

Google App Engineには標準でQueueとWorkerを実行するための仕組みが備わっています。今日はその辺りをまとめていきます。


TaskQueue, PushQueue, PullQueueの違い

私は最初この三つは異なるものだと思っていましたが、TaskQueueの種類としてPushQueuePullQueueがあるという説明が載っていました。ということでPushQueuePullQueueの違いを見ていきましょう。


PushQueue

PushQueueは以下のような仕組みを備えています。

  • ProcessingRateという処理速度を用いてTaskを処理する。
  • App Engineが処理容量に応じて自動的にスケールする。
  • 処理が終わったら自動的にqueueを削除する。

特徴的なところは、

色々な値をデフォルトで設定し(queue.yamlで設定可能)App Engineが自動的に実行し掃除までしてくれる

というところです。

ちなみにApp Engine外のサービスから触ることはできません。


PullQueue

PullQueueは以下のような仕組みを備えています。

  • アプリケーション外のコードや他のアプリケーションがTaskを処理することが可能。
  • 処理時間やタイムフレームを明確に決めることができる。
  • 処理のボリュームにより自分でスケールさせたり、Queueの削除をする必要がある。

一般的なキューイングのシステムはこちらのイメージが近いです。単純なStackのイメージになります。

PushQueueと違い自動で実行はされません。


Go言語でPushQueueを使ってみる

現在開発しているサービスにてPushQueueを使う部分があったので実装例と詰まったところとかを記載していきます。

今回はApp Engineのdelayパッケージを利用しました。

サンプルコード

package main

import (
    "golang.org/x/net/context"
    "google.golang.org/appengine"
    "google.golang.org/appengine/datastore"
    "google.golang.org/appengine/delay"
    "net/http"
)

func init() {
    http.HandleFunc("/", main)
}

var delayPut = delay.Func("delayPut", func(c context.Context, data *Data) {
    keys := Put(c, validData)
})

var Put = func(c context.Context, data *data) []datastore.Key {
    // PutEntity
    keys, err := data.put(c)
    return keys
}

func main(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    delayPut.Call(c, data)
}

こんな感じになりました。

実装上の注意

さて上のサンプルでも利用しているdelayパッケージに関してですが、少し注意点があります。

  • FuncはTop-Levelのコンテキストで呼ぶこと。
    もし仮に、上記mainの関数内でdelayPutを定義したりすると、Task実行時にこのファイルがロードされても、mainの中身までロードする訳ではないため、delayPutが見つからず失敗するためだと思われます。

  • Funcの第二引数の返り値に意味があること。
    Funcの第二引数funcの返り値は複数返すことができますが、最後の値がerror typeであり、かつnilではない場合はもう一度そのメソッドを呼ぶという意味になります。

    実は、PushQueueは失敗したら設定した回数だけリトライされます。

  • テストが少し実装しにくいこと。
    delayFuncを呼んでいるmain()のテストが予想通りになりませんでした。time.Sleepしても、StronglyConsistentDatastore: trueにしても、datastoreへの更新がうまく動かなかったためです。そのため、Put()のように別なメソッドへ切り出して、その部分に対するテストを実装しています。

  • default queueにしかqueueを入れられないこと。
    調べたのですが出てきませんでした。メソッド内に特別入れられるような箇所もなく、delayパッケージではdefault queueにしか積むことが出来ないのかもしれません。おそらくTask Queue APIを利用すれば出来るかと思いますが、今回はそこまで触れませんでした。


queue.yaml

PushQueueの設定をするときは、queue.yamlを作成します。(以下はdefault queueに対する設定を行っています。)

細かい設定内容や詳細はGo Task Queue Configurationに載っています。

queue:
- name: default
  rate: 500/s
  bucket_size: 100
  max_concurrent_requests: 1000

今回はリトライに関するパラメータを一切設定していません。

設定を反映するときは

appcfg.py update_queues myapp/

を実行します。

rate, bucket_size, max_concurrent_requests

設定内容でとても分かりにくかった項目があります。少し私なりの補足を書いていきます。

bucket_size
これから理解するのが一番早いです。bucketとはバケツの意味であり、一度に運べる量みたいなものだと思ってください。(リボルバーの銃弾の装填数の最大数もイメージとして近いです。)
そのため、bucket_size=5のところにqueueが8個きた場合には、5個はすぐに処理用のマシン(プロセッサ)へ渡されますが、残りの3個に関してはbucketに空きが出るまで処理されません。(弾を5発詰めて発射します。残りは3個ですね。)
この仕組みは大量データ(大量queue)に対して、処理量を一様にするためにこのようになっています。もし仮にこの仕組みを採用しないとすると、処理をするマシン(プロセッサ)の数を都度増減させるか、データが最も来るときに合わせてそれを処理できるくらいの数に設定しておかなくてはいけません。
rate
bucket_sizeの空きをどれくらいの頻度・個数空けるかという設定値になります。書き方としては、5/s、10/m、3/hなど個数/[s,m,h]を指定できます。(上のリボルバーでいうと装填速度みたいなものです。)どういった設定値なのかというと、例えばbucket_size=5のところにqueueが8個到達するという場合を考えてみましょう。

rate=1/sの場合 1. bucket = 5, bucket空き = 0, 残りqueue = 3 2. 1s後、bucket = 0, bucket空き = 1, 残りqueue = 3 3. 直後に、bucket = 1, bucket空き = 0, 残りqueue = 2 4. 1s後、bucket = 0, bucket空き = 1, 残りqueue = 2 5. 直後に、bucket = 1, bucket空き = 0, 残りqueue = 1 6. 1s後、bucket = 0, bucket空き = 1, 残りqueue = 1 7. 直後に、bucket = 1, bucket空き = 0, 残りqueue = 0

rate=2/sの場合 1. bucket = 5, bucket空き = 0, 残りqueue = 3 2. 0.5s後、bucket = 0, bucket空き = 1, 残りqueue = 3 3. 直後に、bucket = 1, bucket空き = 0, 残りqueue = 2 4. 0.5s後、bucket = 0, bucket空き = 1, 残りqueue = 2 5. 直後に、bucket = 1, bucket空き = 0, 残りqueue = 1 6. 0.5s後、bucket = 0, bucket空き = 1, 残りqueue = 1 7. 直後に、bucket = 1, bucket空き = 0, 残りqueue = 0

このような流れとなります。bucketの回復速度みたいな感じですね。

公式の説明のキャプチャが分かりやすかったので貼っておきます。
enter image description here
max_concurrent_requests
単純に並列処理をする最大数です。datastoreがたくさんのrequestを受けて競合するのを避けるために使われます。
queue:
- name: optimize-queue
  rate: 20/s
  bucket_size: 40
  max_concurrent_requests: 10

この場合は並列で10ずつしか処理しないので、0.5sに10処理が完了すればqueueが詰まることはありませんね。かつ、並列処理を10に抑えているのでdatastoreの競合が起きにくいです。(多分それくらいなら発生しないかと)


さて、PushQueue, PullQueueに関しては以上です。
PullQueueを利用する機会があれば、そちらの方に突っ込んだ内容を書いてみようかと思います。

では良いインプットと良いプログラミングを。