2017-03-22 22 views
2

私がタグ付けされた労働組合がそのように入力定義することができます。Elmでは、タグ付きの共用体型で比較可能な型を使用する方法は?

type Msg 
    = Sort (Product -> Float) 

しかし、私はそれが好きで定義することはできません。

type Msg 
    = Sort (Product -> comparable) 

エラーは言う:

タイプMsgは型変数の使用を宣言する必要があります比較可能...

しかし、比較可能なのは事前定義された型変数です。

これを修正するにはどうすればよいですか?

+0

elmコンパイラの指示に従って試しましたか? おそらく宣言を次のように変更する必要があります: 'type Msg comparable = ... ' – daniula

答えて

10

この質問はXY Problemのような少しを感じています。私はあなたのメッセージの中でソート関数を渡すことについて別の考え方を提供したいと思います(私はあなたのコードベースに精通していないという注意点、あなたの質問に与えた例のみ)。

タイプパラメータをMsgに追加するのはちょっと面倒なようですが、一歩前に戻ってみましょう。ソートには、同じ型の2つをある方法で比較し、最初の値が2番目の値よりも小さいか等しいか大きいかどうかを返します。 ElmはすでにタイプコンストラクタLT、EQ、GT(Less Than、EQual、およびGreater Than)を持つものを比較するために使用するOrderタイプを持っています。

はのは、次のようにあなたのMsgをリファクタリングしてみましょう:

type Msg 
    = Sort (Product -> Product -> Order) 

今、私たちはMsgに型パラメータを追加する必要はありません。しかし、どのようにして並べ替えるのはProductのフィールドを指定するのですか?私たちはそれをカリングすることができます。

はのは、その最初の引数と同じタイプの他の二つの引数として関数を受け取りcomparingと呼ばれる別の関数を定義してみましょう、とOrder値を返す:最初の引数が関数である

comparing : (a -> comparable) -> a -> a -> Order 
comparing f x y = 
    compare (f x) (f y) 

お知らせをここに方法ですこれは、あなたの例がSortコンストラクタの(Product -> comparable)引数で試行しようとしたものに似ています。それは偶然ではありません。さて、カリングを使用することによって、comparing関数を、.nameまたは.priceのようなレコードフィールドゲッターで部分的に適用することができます。あなたの例を修正するには、onClickハンドラは次のようになります。

onClick (Sort (comparing .name)) 

あなたはこのルートを行く場合は、より多くのリファクタリングがあるでしょう。この比較機能があるので、update関数でどのように使用しますか?ModelList Productproductsというフィールドがあるとします。その場合は、List.sortWith関数を使用してリストを並べ替えることができます。 comparing機能についてこのビジネスはまっすぐ、それは同じことを満たしハスケルから来

case msg of 
    Sort comparer -> 
     { model | products = List.sortWith comparer model.products } ! [] 

少数決算の思考やその他の注意事項:SortMsgのためのあなたの更新の場合は、次のようになります必要。

上記のようにSortコンストラクタを定義するのではなく、私はおそらくそれがそのような共通のイディオムであるため、もう少し抽象化するでしょう。

type alias Comparer a = 
    a -> a -> Order 

type Msg 
    = Sort (Comparer Product) 

そして、ちょうどこのすべてコネクト、 comparing、次の2つのタイプの注釈が同一であるか説明するために、さらに一歩それを取るために:あなたはここに示されているよう Msgを再定義し、その後、このような一般的な機能のためのエイリアスを定義することができます:

-- this is the original example from up above 
comparing : (a -> comparable) -> a -> a -> Order 

-- this example substitutues the `Comparer a` alias, which may help further 
-- your understanding of how it all ties together 
comparing : (a -> comparable) -> Comparer a 
+0

ありがとうございました。これはまさに私が達成したいことですが、やり方を知らなかったのです。私はあなたがここに入れた情報で多くを学んだ。 XY問題のような気がして申し訳ありません。私がパラメータ化された共用体型の問題に直面したとき、私が解決しようとしていた元の問題(ソート関数を渡す)よりも重要であると感じました。しかし、あなたは正しい、それは問題が全体の画像でよりよく説明されるようだ。 –

+0

私はあなたの答えを見る前に、別のメッセージタイプを定義することで問題を解決しようとしました。 "type Msg = SortFloat(Product-> Float)| SortString(Product-> String)"、 "onClick(SortFloat .price)"と "onClick(SortString .name)"を使用します。動作していますが、2つの異なるタイプを処理するために同じロジックを繰り返しているので、良い解決策ではありません。ちょうど私が試したことを共有したいのですが、あなたの答えは間違いなく正しい方法です –

+0

あるいは、あなたのカリングの考えに触発されて、List.sortByをビューレベルで1番目のパラメータで呼び出すことができます。 ( "Sort(List.sortBy .name))"と "onClick(Sort(List.sortBy .price))"のいずれかを選択すると、Msgレベルで変数「Msg = Sort(List Product - > List Product)更新は "f - > {model | products = f model.products}"のソートのケースメッセージです。カッシングの力! –

1

エラーは、comparableがバインドされていない変数タイプであると言います。右側に完全に指定するか(例:Product -> Int)、左側にポリモフィックであることを指定する必要があります。このような何か:

type Msg a = Sort (Product -> a)

あなたはおよそcomparableがここで回答されて尋ねる質問:What does comparable mean in Elm?

+0

ありがとう。しかし、左に指定すると、 "view:Model - > Html(Msg Float)"のようなビューでMsgを使用すると、実際の型を指定する必要があります。最初の場所(私は "onClick(Sort .price)"や "onClick(Sort .name)"のような異なるフィールドで並べ替えることができます。そうすることが可能ですか? –

+0

あなたのモデルがポリモーフィックである必要があるように思えます。 'モデルa = {データ:リストa}' – Joe

関連する問題