プログラマ英語学習日記

プログラミングと英語学習のまとめなど

WWDC What's New In Swift まとめ

早速elmのほうがネタ切れになってきました…やると分かるんですが、 一線を超えると本当に簡単な言語 であまり紹介するTipsがないんですよね。

組み方もいわゆるReduxが強制されますし、Middlewareもフレームワーク内蔵の方法で完結するので、組み方の選択肢があまりないというのも一因です。

ですのでちょっと趣向を変えてネイティブアプリの紹介をしていきたいと思います。

当面のテーマは iOS

WWDCの気になるセッションの概要を書いていきたいと思います。

続きを読む

肥大化するModelTreeとどう戦うか(が分かっていない)

だんだんelmのサンプルが大きくなってきました。Natigationも導入し、いわゆるSPA化してます。

ここで問題になってきたのが、「どう巨大なTreeを管理するか」

言語の難しさの壁を越えたら次は他言語でもあるあるな問題に遭遇しました。

続きを読む

関数型よく知らない人のelm入門 番外編 - 失敗した設計たち

elmを触り始めて2週間ほど、かなりスムーズに組めるようになってきました。

規模が1段階あがるごとに「この組み方じゃダメだ!」となり組み直しているので絶対的なスピードは遅いですけど。

約30ファイル、2000行を超えてきたのでちょっと自分なりに把握したコツ、および失敗例をまとめていきます。

※まだ経験浅いので「ベストプラクティス」というには程遠いかと。

※便宜上、オブジェクト指向の用語を多少用います。

続きを読む

関数型よく知らない人のelm入門(5) - Jsonパース実践サンプル集

フロントエンドのコードを作成するとき、誰もが遭遇するであろう Jsonパース について今回はとりあげたいと思います。

これがなれるまでつらい。

Java等の言語が「Jsonオブジェクトから要素をとりだしていく」のに対し、elmでは「パースロジックを組み上げいく」というフローで実態が見えにくく、理解を困難にしています。

特に「途中経過をログ出力させる」のが難しい(面倒)。パースをミスしたときのエラー原因も分かりづらいです。

そこで今回はまずは組めるようになるという方針でいきます。

題して、JSONパース実践サンプル集

細かい理論は後回し、習うより慣れろ、理解はできた後にやってくるのコンセプトの元、とにかくサンプルコードを書いてみます。

ぜひオンラインエディタで色々編集しながら試してください。

続きを読む

関数型よく知らない人のelm入門(4) - 引数の順序

今回は自分なりにつかんだ elm のコツでも。コツというよりも「オブジェクト指向からの頭の切り替え方」とでもいいましょうか。

最初は引数の順番わけわからん!という状態でしたが、組んでいくうちに「ルール」みたいなものをちょっとずつ掴めてきました。

正しいとはいい切れないかもしれませんが、多少参考になれば。(たぶんですけど関数型全般に共通する要因かと思います)

続きを読む

関数型よく知らない人のelm入門(3) - type alias詳解

毎日ちょっとずつではありますがelmを触ってます。

今回は type alias についてです。

結構クセが強くハマったので自分なりの理解をまとめていきます。

type alias

色んな言語で採用されている同様のものと同じように、 elm においても「型に別名を付ける」のが type alias です。

type alias UserId = Int

getUserNameForId UserId -> Maybe String

こんな感じで使えば「目的」が明確になり、引数や戻り値として分かりやすくなる…といいたいところですが、 elm上では、 type alias で別名宣言しても元の型と同じとしてみなされます

コードを見た方が早いかと。次のような感じです。

intValue : Int
intValue = 3

--普通にビルドが通り動く
getUserForId intValue

こういう宣言をした場合、別の型とみなしてコンパイルを通さない言語もありますが、 elm は通します。 ここ次で大事なのでしっかり把握してください。elmは別名の型は元の型と区別しません。

よって書いておいて何ですが、上記のような使い方はビルドエラー検出としては使えません。(ただし関数の引数・戻り値に使うことで可読性はあがります)


構造体

そこで使われるのが 構造体 として使い方、JavaでいうならEntity-classとしての使い方です。複数のデータを一括して保持するもの、ですね。

type alias Student = 
  { id : Int
  , name : String
  }

myStudent : Student
myStudent = Student 10 "Taro"

--IDと名前を連結する
joinData : Student -> String
joinData s = (toString s.id) ++ ":" ++ s.name

joinData myStudent -- "10:Taro"

こんな感じ。生成部分の表記が特殊ですが、ようは Student という Int と String を受け取る関数があるようなイメージですね。


ここまでは問題ないと思います。では次に同じ情報を保持する Teacher クラスを作ってみましょう。

--Student同様のデータを保持
type alias Teacher = {id: Int, name: String}

myTeacher : Teacher
myTeacher = Teacher 100 "Mike"

ここまではいいと思います。 では次のコードはどうなると思いますか?

--Studentのidとnameを連結する
getStudentData: Student -> String
getStudentData s = (toString s.id) ++ ":" ++ s.name


--それにTeachを渡す
getStudentDatas myTeacher

ビルドエラー?それとも値を出力する?

正解は「値を出力する」です。 100:Mike と表示されます。

このへん、普通の言語の感覚だと苦しみます。だって型が違うのに受け付けて動いているのですから。まるで動的型付け言語のような挙動です。

そこで思いだしてほしいのが最初の UserIdの例

そこで書きましたとおり、type alias はあくまで別名で、元の型が一緒なら区別しない ということです。

つまり、 Student と Teacher は内部的には同一のものとして扱われます。なんとなくこの辺はJaavscriptっぽいですね。

なので、特に型を記述しなくても呼び出せます

getStudentData {id=5, name="Hoge"}  --"5:Hoge"

ここ注意してください。


ただし、構造体の要素が違う場合はビルドが通りません。少ない場合や型が違う場合もダメですし、「要素が多い」場合もビルドエラーです。

--同じように id/nameはあるが一個多い
type alias Master = {id: Int, name:String, level: Int}

myMaster : Master
myMaster = Master 99 "Yoda" 999

getStudentData myMaster -- ビルドエラー

javascriptだと問題なく動くパターンですが、elmではビルドを通しません。MasterとStudentでは、元の型構造が異なるためです。

よって「type aliasとして別々でも、同じ型として扱われる」ことで困ることはあまりないと思います。


値の更新

これに関係してくるのが、 構造体の値の更新 です。

elmでは以下のようにして値を更新します。i

student = Student 1 "Taro"
student2 = {student | id = 2}

このようにすると、name は Taro のまま、 id のみ 2 になります。もちろんelmではこの辺の副作用がないので、 student 自体には影響を与えません。

java的にいうなら、「studentのcloneを作り、そのcloneの値を変更している」イメージです。

ここで問題。次のコードはビルドが通るでしょうか?

student = Student 1 "Taro"

--id が Int でなくString
student2 = {student | id = "2"} 

正解は… ビルドが通ります。実行時もエラーになりません

実はこの場合、 Student ではなく別の新しい型、 {id:String, name:String} が生成されて、student2はこの型になります。

よって次のコードはビルドが通りません。

getStudentData : Student -> String

student = Student 1 "Taro"
student2 = {student | id = "2"} 

getStudentData student2  -- ビルドエラー!

また、「元の構造体にない変数名」はビルドエラーです。

type alias Student = {id : Int, name : String}

student = Student 1 "Taro"

student2 = {student | level = 99} --levelがないのでビルドエラー

トリッキーな挙動ですね。代入する変数の型は違ってもいいのに、名前が違うのはダメという。

しかしこれが問題かというと、まず問題になりません。少なくとも自分はまだ経験していないです。

値更新時は意図しない型になりますが、その更新した値を使おうとするとどこかでビルドエラーになるからです。(上記の例でいう joinData)

なので普通問題にはならないと思います。たとえ型宣言部分を書き替えても、です。(どこかでビルドエラーになるはず)


子要素の更新

また厄介な点として、 構造体ツリーの子要素をダイレクトに更新できません 。以下のような本と出版社の情報を格納する構造体を考えてみましょう。

--出版社と書籍情報
type alias Publisher = { name : String, address : String}
type alias Book = { title : String, publisher : Publisher}

--インスタンス生成
book = Book "Secret" (Publisher "Kado" "Tokyo")

では出版社が引っ越したとします。book を更新しましょう。(※もちろんelmでは値の更新はできないので、値を変更した別インスタンス生成になります)

--ビルドエラー。直接子要素は更新できない
book2 ={book.publisher | address = "Osaka"}

--このパターンもダメ
book3 = { book | publisher = { book.publisher | address = "Osaka" } }

よって、このようなときは以下のように一度要素をバラすしかありません。

publisher1 = book.publisher
publisher2 = { publisher1 | address = "Osaka" }

book2 = { book | publisher = publisher2 }

book2.publisher.address -- Osaka

一旦子要素自体を変数に格納し、それをベースに値を更新。さらにそれを使って Book を更新します。

この程度ならいいですが、階層が深くなると相当面倒でしょうね….

Modelの設計時に注意してください。


まとめ

注意点をまとめますと

  • type alias で別名にしても、元の型が一緒ならelmは区別しない
  • 値更新時、違う型をいれてしまうと別の型としてあつかわれる
  • 一気に子要素を更新できない

この3点に注意しておけばまず困らないと思います。


クセが強いのは事実ですが、本当に学習が楽しい言語ですね。クライアントサイドがメインな都合で Haskell はイマイチ作りたいものがなく挫折したのですが、elmはフロントエンドを組めるので楽しいです。

また、ほとんどの場合でビルドさえ通れば問題なく動くのはちょっとした快感です(css等の表示ミスはありますが、ロジックはまず一発で動く)

フロントエンドの経験がある方前提ですが、Haskellのとっかかりとしてもオススメ。 Haskellほどエレガントではない ですが、理解しづらい部分がそぎ落とされているので入門的な意味では十分かと思います。