Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
    bleis-tift
    @bleis-tift
    ちなみに、判別共用体で複数の値をタプルを使わずに定義した場合、タプルによる構築はできるけど、タプルによる分解はできないという違いがあります
    > type t = Hoge of int * int;;
    
    type t = | Hoge of int * int
    
    > let tpl = 1, 2;;
    
    val tpl : int * int = (1, 2)
    
    > Hoge tpl;;
    val it : t = Hoge (1,2)
    > let (Hoge tpl) = it;;
    
      let (Hoge tpl) = it;;
      -----^^^^^^^^
    
    stdin(5,6): error FS0019: このコンストラクターには 1 個の引数が適用されていますが、必要なのは 2 個です
    つらい
    あ、ミスって消しちゃった・・・
    omanuke
    @omanuke
    読んでる目の前で消えた…(´・_・`)
    bleis-tift
    @bleis-tift
    ごめんなさい><。
    editしようとしてdeleteおしてしもた
    上記エントリに関しての指摘をいくつか連投します。
    まず、パターンマッチの網羅チェックについてですが、「不足の検出」を前面に押し出していますが、個人的には「到達しないケースの検出」も不足の検出と同じくらいに重要だと思っています
    不足している場合は実行時に例外がでますが、到達しないケースの検出は実行時に例外も出ないためより重要と言えるかも
    次に、enumの定義としてtype Modes = LightMode | StandardMode | ...としていますが、これだとenumではなく判別共用体(すべてのケースが値を持たない判別共用体)になります。
    F#ではenumはすべての値を=で指定する必要があります。
    bleis-tift
    @bleis-tift
    enumの網羅性ですが、enumは本体が数値なので、指定したケース以外の値も数値をキャストしたりビット演算することで作り出せます。そのため、0に対応するケースがあったとしても結局網羅性は担保されません
    つまり、コンパイラは他の値の考慮が足りないとして警告を出します。
    次に、F#のタプルですが、Item1Item2によるアクセスはできません
    System.Tuple<...>で表されてはいるのですが、別物と考えておいた方が色々といいかもしれません。
    次は細かいところですが、F#のリストは読み取り専用の順方向リンクリストのようなものではなく、読み取り専用の順方向リンクリストそのものです。
    type 'a list =
      | []
      | (::) of 'a * 'a list
    こんな定義のイメージです
    bleis-tift
    @bleis-tift
    次に「演算子」という言葉の使い方ですが、:?[| ... |]も演算子ではありません。
    演算子であれば( <op> )のようにして関数化可能ですが、どちらも不可能です
    :?はパターン、[| ... |]は配列式を構成する文法です
    Kouji Matsui
    @kekyo
    うほぉ... 詰め甘 enumが判別共用体になっているというのは、あのエラーメッセージから信じ込んでいたんですが、判別共用体が0とみなされる場合があるという事?
    bleis-tift
    @bleis-tift
    判別共用体が0とみなされるとは?
    Kouji Matsui
    @kekyo
    // "警告 FS0025: この式のパターン マッチが不完全です
    // たとえば、値 'enum<Modes> (0)' はパターンに含まれないケースを示す可能性があります。"
    bleis-tift
    @bleis-tift
    それは判別共用体ではなく、enumですね。enumの場合、0のケースがあろうがなかろうが、定義したケースによってすべてを網羅するのは不可能です。ワイルドカードパターンか変数パターンを使う必要があります。
    Kouji Matsui
    @kekyo
    =で値を指定するとenumになり、指定しないと判別共用体となるということで、確かにそうだ...
    到達しないケースの検出、は書いてる途中で気がついてました。改訂するときに盛り込んでみます
    bleis-tift
    @bleis-tift

    次に、:?パターンですが、これも組み合わせる機会はあって、例えばtype t = Hoge of objのような型があったときに、

    let f = function
    | Hoge(:? string) -> "str"
    | _ -> "other"

    のように、何かのパターンとネストして使うことがあります。

    次に複数の値を持ったケースを持つ判別共用体の定義方法ですが、

    // タプルで定義
    type Node =
      | Element of (string * (Node seq))
      | Text of string
    // タプルを使わずに定義
    type Node =
      | Element of string * Node seq
      | Text of string

    の2種類があって、後者の方がより効率的な型を生成します。

    タプルを使うと、生成される型のフィールドには当然タプルが現れますが、タプルを使わない場合はそれぞれ独立したフィールド(上の場合だと2つのフィールド)が直接生成される型のフィールドとして定義されます。
    効率以外にもコード上に現れてくる違いとして、タプルを使わずに定義した場合はmatch式でタプルとして受け取れない、というものがあります。
    bleis-tift
    @bleis-tift
    タプルを使って生成はできるのに対称性が取れてない感じがしますが、おそらくタプルを使って生成できるのは複数引数の関数とタプルの相互変換のほうの理屈からできるようになっていると思われます。
    Kouji Matsui
    @kekyo
    :? の使われる機会があまりないというのは言い過ぎですね。もうちょっと練ってみます。
    bleis-tift
    @bleis-tift
    機能的にはタプルで定義したほうが便利なんですが、実際にはタプルを使わずに定義することの方が多いです。
    Kouji Matsui
    @kekyo
    なるほど タプルで定義するかどうかの部分、混乱を生まずに利点をうまく説明出来るだろうか...
    bleis-tift
    @bleis-tift
    難しいと思いますねw
    Kouji Matsui
    @kekyo

    この辺、検証してて確かに「あれ、これだとダメだな」とか迷ってた記憶がある

    うっ... markdownだった
    bleis-tift
    @bleis-tift
    正直、タプルで定義しない場合でもタプルで受け取れた方がいいと思うんですが、昔その提案をして蹴られたことがあります
    Kouji Matsui
    @kekyo
    理由は何て?
    bleis-tift
    @bleis-tift
    Githubではなく、fsbug(メール)だかなんだかでやり取りしていたころなので、ちょっと思い出せないですね・・・
    Kouji Matsui
    @kekyo
    使い分けれたほうが良い(効率とか?)
    bleis-tift
    @bleis-tift
    User Voiceに挙げてみてもいいかも?
    次に、バナナクリップについてですが、これを「演算子」とするのはF#の演算子と紛らわしいため、できれば避けたい用語です。
    「この括弧はバナナクリップと呼ばれます」くらいにしておいた方が好みです。
    omanuke
    @omanuke
    タプルで定義しない方はフィールド名?つけられるので大抵そっち使ってます
    Kouji Matsui
    @kekyo
    配列と一緒で文法、かな
    bleis-tift
    @bleis-tift
    type t = Hoge of xxx:int * Yyy:string // 大文字始まりにするか小文字始まりにするかで悩む
    大文字派