4

なぜタイプチェックしないextractEitherのですか?

data MyEither a b = MyLeft a | MyRight b
                    deriving (Read, Show)

extractEither :: MyEither a b -> c
extractEither (MyLeft p) = p

コンパイラは次を示します。

Couldn't match type `a' with `c'
  `a' is a rigid type variable bound by
      the type signature for extractEither :: MyEither a b -> c
      at /Users/tongmuchenxuan/playground/test.hs:5:1
  `c' is a rigid type variable bound by
      the type signature for extractEither :: MyEither a b -> c
      at /Users/tongmuchenxuan/playground/test.hs:5:1
In the expression: p
In an equation for `extractEither': extractEither (MyLeft p) = p

`c' はどんな型でも捕まえるのに十分一般的ではありませんか?

4

4 に答える 4

12

いいえ、呼び出し元が関数に返すことを希望する任意の型cです。つまり、関数の場合、常に書き込み可能である必要があります あなたの機能はもちろん許可しません。f :: a -> cf x + 1 :: IntputStr $ f xmain = f x

あなたが望むのは、動的型を返すことです。Haskell は意図的に他の言語ほど簡単にこれを許可していません。なぜなら、返された型が予期しないものである場合、実行時エラーが簡単に発生する可能性があるためです。いろいろな方法がありますが、どれが正しいかは、実際に何をしたいのかによって異なります。文脈を教えていただけますか?あなたの問題に対する最善の解決策は、動的型を使用するのではなく、Haskell の慣用的なものを使用することです。

おそらくあなたが望むのは単純です

extractEither :: MyEither a a -> a
extractEither (MyLeft p) = p
extractEither (MyRight p) = p

両方の「側」の型が同じである必要があります。

于 2012-07-17T08:08:49.530 に答える
11

c基本的に、あなたの型署名は、あなたの関数が与えられた任意の型の値を返す可能性があることを示していますMyEither a b、再び、すべてのaおよびb。ただし、コンパイラが指摘しているように、実際にはタイプの値を返すため( である) c、ここでそのようなものを生成することはできません。ap :: a

MyEither a bさらに、あなたの定義は、あなたがではない場合をまだ管理していませんLeft a。たとえば、あなたが電話したとき、それは何をすべきextractEither (MyRight 1)ですか?そのような関数を実行しようとすると (もちろん型シグネチャを修正した後)、非網羅的なパターンextractEither例外と呼ばれるものが発生する可能性があります。つまり、いくつかの可能な入力パターンを処理するための本体定義がないことを意味します。

MyLeftと の両方の値を抽出する単一の関数を作成しようとしている場合はMyRight、考えを変えてください。左右どちらでも抽出するには、両側で同じ型 (つまりMyEither a a) を使用する必要がありますが、これはあまり役に立ちません。

値を抽出するには、このような関数があることを考慮する必要があります。ここで、たとえば右の値から左の値を抽出しようとする場合のMaybe呼び出しを管理する負担を避けるために戻ります。error

extractMyLeft :: MyEither a b -> Maybe a
extractMyRight :: MyEither a b -> Maybe b

編集:探していた型シグネチャをほぼ取得するのに役立つ可能性のある別のリファクタリングに関する提案については、dblhelix の回答も参照してください。

于 2012-07-17T08:09:00.260 に答える
8

Riccardo の答えに加えて:またはによって構築されているかどうかに関係なくc、値から何らかの型の値を抽出できる関数が必要な場合は、値に type があることを要求するのではなく、関数に「指示」することができますどちらの側からでも型指定された値を取得する方法について:MyEitherMyLeftMyRightMyEither c cc

extractEither :: (a -> c) -> (b -> c) -> MyEither a b -> c
extractEither f g (MyLeft x)  = f x
extractEither f g (MyRight y) = g y

つまり、extractEither2 つの追加の引数fg: 抽出された値を必要な型の値にするために使用される関数を受け取るようになりました。

これは、合計型を扱うときに非常に一般的で便利なイディオムです。

于 2012-07-17T10:58:06.223 に答える
1
extractEither :: MyEither a b -> c
extractEither (MyLeft p) = p

`c' はどんな型でも捕まえるのに十分一般的ではありませんか?

あなたがテーブルに持ち込んでいる直感は、オブジェクト指向言語やサブタイピングを備えた他の言語では機能しますが、Haskell やその他のパラメトリック型付けシステム (Java ジェネリックなど) では機能しません。Java でこのメソッド シグネチャを使用します。

public Object blah(Foo whatever);

このメソッドはObject、階層の最上位の型である を返します。これは、このメソッドの実装が必要な任意の型を返すことを選択できることを意味し、単純に にアップキャストされObjectます。

Haskell 型はそのようには機能しません。の作成者が結果の実行時の型を選択するのと同じように、 の作成者であるがに使用する型を選択できると暗黙的に想定しているため、 はそのままextractFilter返せると考えています。しかし、Haskell では逆です。関数の呼び出し元は、 、およびに使用する型を選択する必要があり、互換性のないandを選択できます。任意の≠を選択した場合、任意の の選択を の任意の選択に変えるために記述できる終了コードはありません。pextractEithercblahabcacacac

于 2012-07-17T17:08:29.133 に答える