LoginSignup
28
18

More than 3 years have passed since last update.

Elm文法総復習

Last updated at Posted at 2019-08-12
1 / 21

はじめに

皆さん、Elm書いてますか?私も最近入門してみたところです。

Elmの文法はHaskellをベースにしているだけあって似たようなところが多く、Haskellの経験がある人にとっては書きやすい言語であると思います。ただ、Haskellの問題点を解決するためであったり、Elm特有の事情のためにHaskellと若干文法が異なっているケースがあります。

そこでこの記事ではHaskellの経験がある人に向けて、ちょっと引っかかりやすいポイントを含めた説明していきたいと思います。

Elmのreplかオンラインエディタを用意して、打ち込みながら試していきましょう!


文法一覧

ではさっそくざっくりと文法の一覧を紹介していきましょう!


リテラル

リテラルにはざっくり以下の3つが存在します。

  • 数値
  • 真偽値
  • 文字

それぞれ詳しく見ていきましょう。


数値リテラル

> 37
37 : number
> 2.718
2.718 : Float

最終的にJavaScriptに変換されるため、Haskellと勝手が異なります。単に整数を記述するとnumberに推論され、その後利用箇所に応じてIntまたはFloatとして扱われます。小数を記述するとFractionalではなくいきなりFloat型に定まります。


真偽値リテラル

> True
True : Bool
> not True
False : Bool

これはHaskellと一緒ですね。大文字から始まるところも、否定に!ではなくnotを使うところも同じです。


文字に関するリテラル

> 'c'
'c' : Char
> "abc"
"abc" : String

シングルクォートがChar型、ダブルクォートがString型なのは一緒です。蛇足ですがHaskellと違ってString型は[Char]のエイリアスではないことに注意してください。

また、複数行文字列リテラルを扱うことができます。ダブルクォート3つで挟んでください。内部でダブルクォートを使用することもできてとっても便利です。ただしReplでは試しにくいです。

"""
Elm Kansai!
Let's "enjoy"!!!
"""

関数定義

square : Int -> Int
square n = n * n

これもHaskellではよく見かける定義方法とほぼ一緒ですね。型を書いてから、関数の中身を定義していきます。ただし先ほどからちょくちょく出てきていますが、型の指定にはひとつだけのコロン:を使います。Haskellではふたつ::だったのでクセが残っている人は超注意です!

また、Haskellと同様にトップレベルで定義する関数にはドキュメントを兼ねて型を書くのがベターです。また、不必要に広い範囲の型推論を避けて、想定外の利用を防止することができるメリットもあります。


Lambda式

f = \n -> n * n

mapの引数とか割といろんなところで使いたくなるLambda式。これもHaskellと一緒で$\lambda$を模したバックスラッシュ\を使って定義します。


リスト

> [1, 2, 3, 4, 5]
[1,2,3,4,5] : List number
> 1 :: [2, 3, 4, 5]
[1,2,3,4,5] : List number
> 1 :: 2 :: 3 :: []
[1,2,3] : List number

カンマ区切りで書けるのは他の言語ともだいたい一緒です。ここでもやはりHaskellと一緒で、空リスト[]に結合関数::でくっつけていく方式でリストが成り立っています。ただしHaskellではリストの先頭に要素を結合する関数が:だったのに対してElmでは::になっていることに注意が必要です。関数と逆ですので!


タプル

t = (False, "Foo", Just 5)
(name, age) = ("Thomas", 37)

リストと違って固定長で、中身の要素は型が違っても大丈夫なのがタプルです。これもHaskellとの違いは無いので大丈夫でしょう。


レコード

type alias Point =
  { lat : Float
  , lon : Float
  }

p1 = 
  { lat = 139.744858
  , lon = 35.675888
  }

Haskellではdataでレコードを作りますが、Elmでは型エイリアスで定義します。あとは代入したり、関数の型に書いたりして使うだけです。

また、レコード内の値の読み取りは.field_nameでアクセスします。これは関数として定義されますが、JS風に後ろにくっつけることもできます。レコード内のフィールドと同名の値取り出し用関数が定義されるHaskellとは異なるところですので注意が必要ですね。例えば今回の場合は以下のようにアクセスします。関数としても使えるので、mapの引数にも簡単に与えられます!

> .lat
<function> : { b | lat : a } -> a
> p1.lat
139.744858 : Float
> .lat p1
139.744858 : Float
> List.map .lat [p1]
[139.744858] : List Float

ただし注意が必要な点として.latは「何でも良いけどlatを含むレコードの中にあるlatの値を取ってくる」関数であって「Pointだけを引数に取る」関数ではないというところです。Pointとは全然関係ないlatというフィールドを持ったレコードに対しても使用できてしまいます。

> a2 = {lat = "FOOOOO!!"}
{ lat = "FOOOOO!!" } : { lat : String }
> .lat a2
"FOOOOO!!" : String

if式

n = 5
s = if (n < 5) then "It's small" else "It's big!"

おなじみif式です。elseが必須であること、返す値の型を全て同一にしなければならないことも含めてHaskellとの違いも特にありません。筆者は他の言語と行ったり来たりしているうちによくthenを書き忘れてしまいがちなので、皆さんもお気を付けください。


case式

maybeInt = Just 5
m = case maybeInt of
  Just n -> n
  Nothing -> 0

これもHaskellと一緒です。中でパターンマッチを使うこともできるのでMaybeやList、レコードを使うときなどに重宝するでしょう!


let-in式

let
  a = (2 - 1) ^ 2
  b = (3 - 5) ^ 2
in
  sqrt (a + b)

これもHaskellでおなじみinの中で使う値をletで計算しておける便利構文です。なおElmではlet-in式で十分という理由1からwhereを実装していません。今後もおそらく実装されない見通しなので諦めましょう。


カッコの省略

> List.map ((+) 10) (List.filter ((>) 5) (List.range 1 10))
> List.map ((+) 10) <| List.filter ((>) 5) <| List.range 1 10
[11,12,13,14]

ちょっと文法とは異なりますがせっかくなので紹介。カッコばかりで見づらくなるためHaskellでよく使う($)はElmでは<|と書きます。右から順番に適用していくイメージですね。


パイプ

> 5 |> (\n -> n + 10) |> String.fromInt
"15" : String

先ほどのものと対になるこちらも紹介。Unixでおなじみのパイプを再現したような動きをするパイプ|>がElmにはあります。実はHaskellにもData.Function(&)という関数が定義されていて同じ事ができます2

個人的に、Elmではどちらかというと左から自然に読めるパイプの方が好まれている印象を受けました。


コメント

-- 1 line comment
{-
Multiple
Lines
Comment
{- and nested! -}
-}

これは完全にHaskellと同一ですね。ネストできるところも使いどころによっては便利かもしれないです。


型クラス

> List.map (\n -> n + 1) (List.range 1 10)
[2,3,4,5,6,7,8,9,10,11]
> Maybe.map (\n -> n + 1) (Just 5)
Just 6

Elmにはいわゆる型クラスに相当するものが実装されていません。個別のクラスそれぞれでmapfilterなどが定義されています。従って先ほどからちらほら出てきているように、利用時にはプレフィックスが必要となります。

ただし例外的にappendable, comparable, numberの型はいわゆる型クラス的な動きをします3。しかし自作型をそれらのサブクラスにしたりすることはできません。また、自分でこのような働きをするクラスを定義することもできません。

> (++)
<function> : appendable -> appendable -> appendable
> [1, 2, 3] ++ [4, 5, 6]
[1,2,3,4,5,6]
    : List number
> "Foo" ++ "Bar"
"FooBar" : String
> (>)
<function> : comparable -> comparable -> Bool
> "foo" > "bar"
True : Bool
> 1 > 2
False : Bool
> (+)
<function> : number -> number -> number
> 1 + 2
3 : number
> 1.5 + 5.3
6.8 : Float

おわりに

元々Haskell界隈からこの言語を知ったので、主にHaskellとの違いに触れつつ文法に関する事項について整理してみました。ほぼ一緒なところもありつつ、微妙に違ったりするところもあるのでHaskellから移ってきた人は若干注意が必要かもしれません。

JSや設計思想との兼ね合いでHaskellからは思い切って機能を削ってきているところもありますが、Webフロント開発をする上で十分であろうとは思います。そういうのをバリバリ使いたい人はGHCJSなどを検討すると良さそうです。

とにもかくにもReplで気軽に試しつつ、Elmの世界に浸ってもらえればと思います!


  1. Why does Elm support ‘let’ but not ‘where’? 

  2. 今回ちゃんと調べて初めて知りました。Hoogleに載ってます。 

  3. このほかにcompappendというListとStringを統一的に扱う型が定義されていますが、コンパイラが使うだけでユーザー側から扱うことはありません。 

28
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
18