2011-03-04 7 views
31

私の自由な時間に私はHaskellを学んでいるので、これは初心者の質問です。私はEither aFunctorのインスタンス作られる様子を示した例に出くわした私の読書でFunctorのインスタンスの理解方法

:今

instance Functor (Either a) where 
    fmap f (Right x) = Right (f x) 
    fmap f (Left x) = Left x 

は、私がRight値コンストラクタの場合の理由を実装マップを理解しようとしています、 Leftの場合はありませんか?ここで

は私の理解です:

まず私は

instance Functor (Either a) where 
    fmap g (Right x) = Right (g x) 
    fmap g (Left x) = Left x 

として上記のインスタンスを書き換えてみましょう:

  1. 我々はfを置き換えた場合、私はそのfmap :: (c -> d) -> f c -> f d

  2. を知っていますEither a我々が得るfmap :: (c -> d) -> Either a c -> Either a d

  3. Right (g x)のタイプはEither a (g x)で、g xのタイプはdあるので、我々はRight (g x)の種類は、我々は(上記の2を参照)fmapから期待するものである、Either a dであることを持っている

  4. 我々はLeft (g x)を見れば今、私たちは、それは我々が(上記の2を参照)fmapから何を期待されていない、Either d bあり、その種類はEither (g x) bであると言うことは同じ推論を使用することができます。dは、二番目のパラメータでなければなりません、最初のものではありません!したがって、Leftにマップすることはできません。

私の推論は正しいですか?

+11

いずれかはおそらくより明らかBifunctorファンクタより - 2領域マップ:: - ( - > M) - Bifunctor操作2領域マップを有し>( b→n)→fab→fm nとなる。これにより、左と右の両方の場合のマッピングが得られます。ハスケルの標準ライブラリにはBifunctorクラスがありません。これは、 "野生の" FunctorよりもBifunctorsの方がずっと少ないためです。どちらかとペア(a、b)には便利ですが。 –

+1

3では、型の一部として(g x)を使用しますが、値です。それは '(g x)'ではなく、 'typeof(g x)'を書くことを意味しています。 – Peaker

+0

@stephen tetley:それは興味深い!ありがとう – MarcoS

答えて

20

これは正しいです。この動作の別の非常に重要な理由もあります。を計算として考えてください。成功する可能性があり、bを返すか、エラーメッセージaで失敗します。 (これは、モナドのインスタンスがどのように動作するかです)。演算子をマップしたいので、ファンクタインスタンスがLeftの値に触れないことは当然です。計算が失敗した場合は、操作するものは何もありません。

+0

私はまだモナドについて学んだことはありません:)あなたが記述する方法では「どちらかab」が使用されており、「a」はエラーメッセージを表しています。 – MarcoS

+0

@マルコ:正しい、これは慣例に過ぎない。いずれかは、分離として解釈される可能性があります。型の値a bには、型aの値または型bの値が含まれますが、両方は含まれません。 –

+0

個々のケースで - "いずれかの文字列" "どちらかの文字列文字"、 "どちらかの文字列int"など - これはもちろんです。左右の型に違いはありません。しかし、ファンクタとして、fmap over - (これらの例ではString)として、完全な非対称性があります。具体的なFunctorインスタンス(ここでは 'String')に入る型は、あらゆる型の普通の計算で見つけることができる一般的なものを表す必要があります(これは' fmap'を介して右に起こります)。解釈は、唯一のものではなく、事故ではありません。 – applicative

8

あなたのアカウントは当然です。たぶん、このようなインスタンスでは難しい理由は、可能な限りタイプごとに1つずつ、無限に多くのファンクタインスタンスを定義するということです。しかし、Functorインスタンスは、システム内の無限に多くの型を操作する体系的な方法です。したがって、システム内の無限に多くの型に対して体系的に操作する無限に多くの方法を定義しています。この例は、2つの方法で一般性を含む。

あなたが段階的にそれをとっても、それほど奇妙ではないかもしれません。これらのタイプの最初は、ユニット型()とその唯一の合法的な値()を使用してMaybeのlongwindedバージョンです:

data MightBe b  = Nope() | Yep b 
data UnlessError b = Bad String | Good b 
data ElseInt b  = Else Int | Value b 

ここでは、疲れや抽象化したりすることがあります:

data Unless a b = Mere a  | Genuine b 

は、今では作ります私たちのFunctorのインスタンスは、問題なく最初のインスタンスは、Maybeのインスタンスのように見える:

instance Functor MightBe where 
    fmap f (Nope()) = Nope() -- compare with Nothing 
    fmap f (Yep x) = Yep (f x) -- compare with Just (f x) 

instance Functor UnlessError where 
    fmap f (Bad str) = Bad str -- a more informative Nothing 
    fmap f (Good x) = Good (f x) 

instance Functor ElseInt where 
    fmap f (Else n) = Else n 
    fmap f (Value b) = Value (f b) 

しかし、再び、なぜわざわざ、のは、抽象化してみましょう:

instance Functor (Unless a) where 
    fmap f (Mere a) = Mere a 
    fmap f (Genuine x) = Genuine (f x) 

()StringInt値は触れていなかったとしてMere a用語は、触れられていません。

+0

ありがとう、これはまた非常にいい説明です。しかし、私はFUZxxlの投稿を答えとして受け入れました。なぜなら、自分の推論を確認するだけでなく、「どちらかb」を計算(モナド)として解釈することに興味深いヒントを与えるからです。 – MarcoS

1

今、私は 右値コンストラクタの場合は、なぜ 実装マップを理解しようとしているが、 は、左の場合にはいないのですか?

ここに差し込むと意味があります。

アサイン= a文字列(エラーメッセージ) フロートにどちらかを適用します。

f:Float - > Integerは、例えば丸めなどとなります。

(いずれかの文字列)(浮動小数点数)=いずれかの文字列浮動小数点。

now(fmap f)::いずれかの文字列浮動小数点 - >どちらかの文字列整数 fとは何をするつもりですか? fは文字列で何をすべきかわからないので、何もできません。つまり、で、明らかにです。あなたが実行できるのは、左の値を変更せずに正しい値にすることだけです。他の人が述べたように右の値について

  • は、左の値のF
  • を適用何もしない
4

:によって与えられ、このような明白なFMAPがあるので、言い換えれば

はどちらかファンクタであります、Eitherタイプは両方の引数のファンクタです。しかし、Haskellでは、型の最後の引数でファンクタのみを(直接的に)定義することができます。このようなケースでは、我々はnewtype Sを使用して制限を回避することができます

newtype FlipEither b a = FlipEither { unFlipEither :: Either a b } 

だから私たちは newtypeとスワップ型引数に Eitherをラップコンストラクタ FlipEither :: Either a b -> FlipEither b aを持っています。そして、それをアンラップするデクストラクタ unFlipEither :: FlipEither b a -> Either a bがあります。ただ、私たちはしばらくの間、 FlipEitherを忘れてしまった場合、我々は Eitherため Functorだけの定義を取得することを

instance Functor (FlipEither b) where 
    fmap f (FlipEither (Left x)) = FlipEither (Left (f x)) 
    fmap f (FlipEither (Right x)) = FlipEither (Right x) 

お知らせ:今、私たちは、sの最初の引数「は実際にはEitherでの最後の引数、」FlipEitherでファンクタのインスタンスを定義することができますLeft/Rightをスワップします。そして、今度は、Functorインスタンスがにラップされた後に、それをアンラップすることができるEitherのインスタンスではいつでも、たとえば、次のように

fmapE2 :: (a -> b) -> Either a c -> Either b c 
fmapE2 f = unFlipEither . fmap f . FlipEither 

更新:は、Data.Bifunctor見を持っているのEither(,)のインスタンスです。各bifunctorには2つの引数があり、それぞれのファンクタです。これはBifunctorの方法firstsecondに反映されています。

EitherBifunctorの定義は非常に対称型である:

instance Bifunctor Either where 
    bimap f _ (Left a) = Left (f a) 
    bimap _ g (Right b) = Right (g b) 

    first f = bimap f id 

    second f = bimap id f 
関連する問題