2016-01-05 6 views
19

私は、入力として値をとり、その入力の関数を呼び出し、その結果がJust xなら、それはxを返さなければなりません。それ以外の場合は、元の入力を戻す必要があります。言い換えればapはどのように構成されますか?

、この機能(私がコールするのか分からなかったこと):それはまだ、定義されていない場合

foo :: (a -> Maybe a) -> a -> a 
foo f x = fromMaybe x (f x) 

それは、汎用機能のように思えるので、私は疑問に思いましたI asked on TwitterChris Allen repliedap fromMaybeです。

Prelude Control.Monad Data.Maybe> :type ap 
ap :: Monad m => m (a -> b) -> m a -> m b 
Prelude Control.Monad Data.Maybe> :type fromMaybe 
fromMaybe :: a -> Maybe a -> a 
Prelude Control.Monad Data.Maybe> :type ap fromMaybe 
ap fromMaybe :: (b -> Maybe b) -> b -> b 

ap fromMaybeの種類は確かに正しい見え、実験のカップルは、それが同様に望ましい行動を持っていることを示しているように見える:私はGHCiのを解雇し、実験を始め、有望な響き

ただし、どのように動作しますか?

fromMaybe機能は、私には明確なようで、単独で、私はapが何をするかを理解すると思う - 少なくともMaybeの文脈で。 mMaybeの場合、タイプはMaybe (a -> b) -> Maybe a -> Maybe bです。

私が理解できないことは、どのようにap fromMaybeがコンパイルされるかです。私には、この表現は部分的なアプリケーションのように見えますが、間違っている可能性があります。しかし、この場合、タイプがどのように一致するかはわかりません。

apの最初の引数はm (a -> b)ですが、fromMaybeの型はa -> Maybe a -> aです。それはどのように一致しますか?どのMonadインスタンスは、コンパイラはmが推測するのですか? fromMaybeは、2つの(カレー化された)引数をとると、1つの引数をとる関数にどのように変換されますか?

誰かがドットを結ぶのを手助けできますか?

+5

これは私を夢中にさせてくれて嬉しく思っています。x.x – dcastro

+1

ところで、 'ap fromMaybe'は本当のプログラムに誰も書いてくれないだろうという難読化されたコードです。 –

答えて

16

寛解と機械的な答えのお詫び。私はApplicativeやMonadのようなチェリーピッキングのものは好きではありませんが、どこにいるのかはわかりません。これはnot my usual approach to teaching Haskellです。

まず、apは実際にはフードの下で(<*>)です。

これはどういう意味ですか?つまり、私たちが何をしているのかを記述するために、モナドのように「強い」ものが必要なわけではありません。適用は十分です。しかし、Functorはしません。 、把握する

Prelude> ap (Just (+1)) (Just 1) 
Just 2 
Prelude> (<*>) (Just (+1)) (Just 1) 
Just 2 

まず最初は、ある型クラスのApplicativeのどのインスタンス私たちは話している。

Prelude> :info Applicative 
class Functor f => Applicative (f :: * -> *) where 
    pure :: a -> f a 
    (<*>) :: f (a -> b) -> f a -> f b 
Prelude> :info Functor 
class Functor (f :: * -> *) where 
    fmap :: (a -> b) -> f a -> f b 

ここではたぶんモナド/ Applicativeのでap/(<*>)ですか?

Prelude> :t fromMaybe 
fromMaybe :: a -> Maybe a -> a 

脱糖fromMaybeのタイプは少しは私達を与える:

(->) a (Maybe a -> a) 

だから私たちはここに関係しているタイプのコンストラクタは(->)です。 GHCiから関数型としても知られている(->)について教えてください。

Prelude> :info (->) 
data (->) a b -- Defined in ‘GHC.Prim’ 
instance Monad ((->) r) -- Defined in ‘GHC.Base’ 
instance Functor ((->) r) -- Defined in ‘GHC.Base’ 
instance Applicative ((->) a) -- Defined in ‘GHC.Base’ 

Hrm。たぶんどうですか?

Prelude> (+1) 1 
2 
Prelude> (+1) `fmap` Just 1 
Just 2 
Prelude> Just (+1) <*> Just 1 
Just 2 
Prelude> :t fmap 
fmap :: Functor f => (a -> b) -> f a -> f b 
Prelude> let mFmap = fmap :: (a -> b) -> Maybe a -> Maybe b 
Prelude> (+1) `mFmap` Just 1 
Just 2 
Prelude> :t (<*>) 
(<*>) :: Applicative f => f (a -> b) -> f a -> f b 
Prelude> let mAp = (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b 
Prelude> :t (+1) 
(+1) :: Num a => a -> a 
Prelude> :t Just (+1) 
Just (+1) :: Num a => Maybe (a -> a) 
Prelude> Just (+1) `mAp` Just 1 
Just 2 

さて、どのような関数型のファンクタとApplicativeの程度:

たぶんため (<*>)を利用して何が起こっ
Prelude> :info Maybe 
data Maybe a = Nothing | Just a  -- Defined in ‘GHC.Base’ 
instance Monad Maybe -- Defined in ‘GHC.Base’ 
instance Functor Maybe -- Defined in ‘GHC.Base’ 
instance Applicative Maybe -- Defined in ‘GHC.Base’ 

は、このでしたか?ここで難しい部分の1つは、(->)が部分的にのに適用され、Functor/Applicative/Monadとなることです。したがって、f(->) a b(->) aになります。ここで、aは引数型であり、結果はbです。

Prelude> (fmap (+1) (+2)) 0 
3 
Prelude> (fmap (+1) (+2)) 0 
3 
Prelude> :t fmap 
fmap :: Functor f => (a -> b) -> f a -> f b 
Prelude> let funcMap = fmap :: (a -> b) -> (c -> a) -> c -> b 
Prelude> -- f ~ (->) c 
Prelude> (funcMap (+1) (+2)) 0 
3 

Prelude> :t (<*>) 
(<*>) :: Applicative f => f (a -> b) -> f a -> f b 
Prelude> let funcAp = (<*>) :: (c -> a -> b) -> (c -> a) -> (c -> b) 
Prelude> :t fromMaybe 
fromMaybe :: a -> Maybe a -> a 
Prelude> :t funcAp fromMaybe 
funcAp fromMaybe :: (b -> Maybe b) -> b -> b 
Prelude> :t const 
const :: a -> b -> a 
Prelude> :t funcAp const 
funcAp const :: (b -> b1) -> b -> b 

有用であるとは限りません。 funcAp constは、型からだけでなく、パラメトリシティの仕組みを知ることができます。

編集:作成者については、ファンクタ(->) aはちょうど(.)です。それが適用されますが、追加の引数があります。 MonadはApplicativeですが、引数が反転されています。

さらなるwhuttery:適用の場合)はSKIコンビネータ計算のKであり、pureはKです。あなたは中置構文を好むならば、あなたが探しているモナドが(->) rまたはr -> _ある

Prelude> :t pure 
pure :: Applicative f => a -> f a 
Prelude> :t const 
const :: a -> b -> a 
Prelude> :t const 
const :: a -> b -> a 
Prelude> let k = pure :: a -> b -> a 
Prelude> k 1 2 
1 
Prelude> const 1 2 
1 
+4

この回答は簡素でも機械的でもありません。それは活発で詳細です! –

+3

ありがとう、とても助かりました。特に私の理解を深めるのに役立つ 'mFmap'、' funcMap'、 'funcAp'の定義が見つかりました。 –

19

しかし、apの使用はMaybeという文脈ではではなく、である。それは

ap :: Monad m => m (a  -> b) -> m a -> m b 
fromMaybe  :: r -> (Maybe r -> r) 
ap   :: (r -> (a  -> b)) -> (r -> a) -> (r -> b) 
ap     f      g  x :: b 
ap fromMaybe ::       (r -> a) -> (r -> b) , a ~ Maybe r , b ~ r 
である私たちは

instance Monad ((->) r) 

を持っている私たちは、機能、fromMaybeでそれを使用しているので、機能の状況、様々なMonadインスタンスの中

ap f g x = f x (g x) 

にあります

タイプの->は右に関連付けられているため、a -> b -> c ~ a -> (b -> c)です。タイプをまとめてプラグインしようとすると、上の定義に達することができます。

そして(<*>) :: Applicative f => f (a -> b) -> f a -> f bで、我々はあなたが落書きのこの種のような場合には、(fromMaybe <*>)としてそれを書くことができます。

#> :t (fromMaybe <*>) 
(fromMaybe <*>) :: (r -> Maybe r) -> r -> r 

当然ここで別の答えに記載されたような機能を使用する場合、<*>だけですあなたの良いole 'S combinator。 HaskellではSという名前の関数を持つことはできませんので、<*>はポイントフリーのコーディングスタイルの標準レパートリーの一部です。単項バインド=<<、(より多くのように、反転し)、さらに神秘的なことができますが、pointfreeコーダはcombinatory関数呼び出しでは、

(f =<< g) x = f (g x) x 

を単に気にしないと喜んで、それは別の、同様のパターンをエンコードするために使用されます、謎や謎(zipWith (-) =<< drop 1が気になる)。

8

わかりやすくするために、型引数のラベルを変更します。モナドのインスタンスであるmは、コンパイラの推論を行い

ap :: Monad m => m (a -> b) -> m a -> m b 
fromMaybe :: c -> Maybe c -> c 

((->) r)は、Monadである。これは、型がrを引数として持つ関数です。一部はrです。だから、タイプで

(c ->)
ap :: Monad m => m (a -> b) -> m a -> m b 

m、〜Maybe caと〜cb

返品タイプm a -> m bは、ap fromMaybeのタイプである(c -> Maybe c) -> c -> cに展開されます。

6

を(あなたは私が実際にあなたがKおよびSから任意のプログラムを導くことができるKおよびSから派生することができます)。

はその後apの署名が拡大へ:今、あなたは部分的に適用される関数としてap fromMaybe考えると出来上がりあなたが希望する結果を取得する場合

m (a -> b) -> m a -> m b = 
(r -> (a -> b)) -> (r -> a) -> r -> b = -- now we use the signature of fromMaybe 
(b -> (Maybe b -> b)) -> (b -> Maybe b) -> b -> b 

関連する問題