ホームページ  >  記事  >  ウェブフロントエンド  >  Elm 入門演習 -- Type_html/css_WEB-ITnose

Elm 入門演習 -- Type_html/css_WEB-ITnose

WBOY
WBOYオリジナル
2016-06-24 11:15:391059ブラウズ

Facebook がかつてコミュニティ イベントで、Javascript をどんどん使っていくうちに、すぐに PHP で遭遇したのと同じ問題に直面するようになった、と言ったのを覚えています。

動的言語は諸刃の剣のようなもので、その柔軟性を気に入っていただける一方で、小さな見落としによって大きな損失を被る可能性もあります。 Elm は、通常、ほとんどの関数型言語で選択される静的強力な型付けを選択しました。オブジェクト指向言語のクラスの概念がなければ、強力な型システムがすべての「これは何ですか?」という質問を解決する役割を果たします

型アノテーション

も可能です。 Elm は型シグネチャと呼ばれ、コロン: を使用して型を示します。Hello world に基づいて、変数と関数をそれぞれ定義し、型を示します

import Html exposing (..)import Html.Attributes exposing (..)elm : Stringelm = "elm"sayHello : String -> StringsayHello name =   "Hello, " ++ namemain =  div [class "hello"]     [ span [] [text (sayHello elm)]    ]

elm "elm" の値を数値に変更して見てください。それが機能する場合、何が起こったのでしょうか?

Detected errors in 1 module.-- TYPE MISMATCH ---------------------------------------------------------------The type annotation for `elm` does not match its definition.5| elm : String         ^^^^^^The type annotation is saying:    StringBut I am inferring that the definition has this type:    number

コンパイラはエラーを見つけ、特定の行番号を見つけることができました。

型が宣言されていない場合はどうなりますか?型アノテーション

import Html exposing (..)import Html.Attributes exposing (..)--elm : Stringelm = 6--sayHello : String -> StringsayHello name =   "Hello, " ++ namemain =  div [class "hello"]     [ span [] [text (sayHello elm)]    ]

をコメントアウトして再コンパイルすると、引き続きエラーが報告されますが、エラー メッセージが変更されています。今回は 14 行目です:

Detected errors in 1 module.-- TYPE MISMATCH ---------------------------------------------------------------The argument to function `sayHello` is causing a mismatch.14|                      sayHello elm)                                  ^^^Function `sayHello` is expecting the argument to be:    StringBut it is:    number

明示的な型アノテーションがなくても、Elm の型推論システムはこれは型推論により、sayHello 関数のパラメータは文字列であるはずだと考えられますが、数値が渡されるため、エラーが報告されます。

2 つの異なるエラー プロンプトを比較すると、型アノテーションによりコンパイラーがエラーをより正確に見つけて特定できることがわかります。学習が進むにつれて、型システムによってもたらされる安心感が徐々に好きになるでしょう。コンパイルが失敗した場合、明確なプロンプトが表示されるため、問題をすぐに特定できます。そして、コンパイルが成功する限り、プログラムは確実に実行されます。

基本型とList

基本型

基本型はほとんどの言語に似ており、String、Char、Bool Int、Float にすぎません。公式 Web サイトのリテラルを参照できます。 Elm では、String は二重引用符を使用する必要があり、Char を表すには単一引用符を使用する必要があることに注意してください。

List

厳密に言えば、List は型ではありません。その型は List a です。ここで、a は型変数と呼ばれます。これは、List がコンテナであり、String、Int、または何も保持できないため、型は次のとおりである必要があります。動的:

> [ "Alice", "Bob" ][ "Alice", "Bob" ] : List String> [ 1.0, 8.6, 42.1 ][ 1.0, 8.6, 42.1 ] : List Float> [][] : List a

型変数については後で引き続き説明します。ここで覚えておく必要があることは 1 つだけです。

List は型ではなく、List String のようなものは

です。 パラメータが 1 つしかないため、Elm の List は 1 つの型の要素のみを収容できます。Javascript の List とは異なり、すべての要素を受け入れます。次のものがコンパイラによって検出され、報告されます。

list = [1, "a"]

型エイリアス

使用される型エイリアス

type alias Name = Stringtype alias Age = Int  type alias User = {name: Name, age: Age}user : Useruser =  { name = "Zhang zhe", age = 89 }  setUserName : String -> User -> UsersetUserName name user = {user | name = name}

などの既知の型を組み合わせたり再利用したりするため、基本的な型にビジネス セマンティクスを持たせるだけでなく、複雑なデータ構造に適したセマンティクス型を組み合わせることができます。エイリアスがない場合、setUserName の型シグネチャは次のように記述する必要があります... 塊:

setUserName : String -> {name: String, age: Int} -> {name: String, age: Int}

Union 型

Union 型は、Elm 型システムの最も重要な部分の 1 つです。可能な値のセットの場合、各 A 値は次のようにタグと呼ばれます:

type Bool = True | Falsetype User = Anonymos | Authed

その中で、True と False、Anonymos と Authed はすべてタグ名です (

Tag は Type ではないことに注意してください

)。列挙に似ていますか?それだけでなく、Union 型の利点は、Tag が既知の型のセットを保持できることです。上記のコードでは 2 つのタイプのユーザーを区別できますが、現時点では、認証されたユーザーの名前を取得することはできません。既知のタイプを Tag と組み合わせて次のように表現できます。

type User = Anonymos | Authed String
Union タイプを作成するとき、実際にはタグごとに共用体型を作成します。対応する値コンストラクターが作成されます:
> type User = Anonymous | Authed String> AnonymousAnonymous : User> AuthedAuthed : String -> User

他の情報のない匿名は値として直接使用できますが (True と False を考えてください)、既知の型を持つ Authed は実際には受け入れる関数です。文字列を返し、ユーザータイプ:

users : List Userusers = [  Anonymous,   Authed "Kpax"]

Haskell にはタグの名前はありません。これに似たものは値コンストラクターと呼ばれ、その目的を直接示します: この型の値を構築するため

タグは分解することもできます :

getAuthedUserName : User -> StringgetAuthedUserName user =  case user of     Anonymous ->      ""    Authed name ->      name
この関数は、認証されたユーザーの名前を返します。匿名ユーザーの場合は空の文字列を返します。

オンライン エディターで実行できる完全なコードは次のとおりです:

import Html exposing (..)import Listtype User = Anonymous | Authed Stringusers : List Userusers = [  Anonymous,   Authed "Kpax",  Authed "qin"]getAuthedUserName : User -> StringgetAuthedUserName user =  case user of     Anonymous ->      ""    Authed name ->      name      main =  div [] (List.map (text << getAuthedUserName) users)

text << getAuthedUserName は、lodash の _ と同様に、Elm の << 演算子を使用します。 flowRight 関数

型変数

型のリストについては上で説明しましたが、a は型変数であり、オブジェクト指向プログラミングにおけるジェネリックスの概念と同様に、現在不確実な型を表します

型シグネチャマップ関数の Type 変数も使用されます:

map : (a -> b) -> a -> b

これにより、ユーザーの型が User である限り、map 関数は特定の型を気にせずに、map userToString user を呼び出すことができます。

では、List a 型を定義するにはどうすればよいでしょうか?コードは次のとおりです

type List a = Empty | Node a (List a)

前面说到 Tag可以携带已知类型,那么是否可以携带正在定义的这个类型呢?答案是肯定的!这就是类型的 递归, List a就是这样一个带有类型参数的递归类型,平时我们写的数组,可以理解为如下代码的语法糖

-- []Empty-- [1]Node 1 Empty-- [1, 2, 3]Node 1 (Node 2 (Node 3 Empty))

同样的思路,我们完全可以自己实现二叉树等数据结构,有兴趣的朋友不妨试试,官方文档有 相关章节可供参考

Counter with type

上一章[基础篇]()我们讲了Counter的实现,代码如下:

import Html exposing (..)import Html.Events exposing (onClick)import Html.App as Apptype Msg = Increment | Decrementupdate msg model =   case msg of     Increment ->       model + 1    Decrement ->      model - 1view model =  div []    [ button [onClick Decrement] [text "-"]    , text (toString model)    , button [onClick Increment] [text "+"]  ]initModel = 3main = App.beginnerProgram {model = initModel, view = view, update = update}

让我们用刚刚学习的知识给以上代码添加类型和类型注解

首先,我们有 initModel这个数据,它的类型是 Int,不具备任何业务语义,让我们定义一个类型别名 Model来表示Counter的数据

type alias Model = Int

自然 initModel的类型应该为 Model

initModel : ModelinitModel = 3

update函数的类型签名比较简单,它接受消息 Msg和当前数据 Model,返回新的数据 Model:

update : Msg -> Model -> Model

view函数接受 Model类型的数据,返回什么呢?如果查阅 div函数的 文档,你会发现返回的是一个带有类型变量的类型 Html msg。其实很好理解,因为渲染界面的函数不仅要输出Html,当事件发生时还要输出 消息,输出消息的类型,就是应该赋给变量 msg的类型,在 Counter中消息的类型是 Msg,因此:

view : Model -> Html Msg

完整代码:

import Html exposing (..)import Html.Events exposing (onClick)import Html.App as Apptype alias Model = Inttype Msg = Increment | Decrementupdate : Msg -> Model -> Modelupdate msg model =   case msg of     Increment ->       model + 1    Decrement ->      model - 1view : Model -> Html Msgview model =  div []    [ button [onClick Decrement] [text "-"]    , text (toString model)    , button [onClick Increment] [text "+"]  ]initModel : ModelinitModel = 3main = App.beginnerProgram {model = initModel, view = view, update = update}

总结

类型的学习可能有些枯燥,但是非常重要。如果你了解redux,你会发现Union type简直天生就是做action的料,比起redux在javascript中使用的字符串既简洁又达意,甚至还可以嵌套组合,谈笑风生!高到不知道哪里去了!

下一章我们将把在线编辑器放到一边,把Counter迁移到本地运行,然后实现一个CounterList,在CounterList中,你会看到Elm是如何复用组件,以及为什么Elm被称为理想的 分形架构。

各种架构对比,可以参考Cycle.js作者Andre Staltz的 文章 `elm

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。