简体   繁体   English

使用Applicative和Functor的Haskell函数

[英]Haskell function using Applicative and Functor

I have a practice question, where I'm given a function: 我有一个练习题,给我一个函数:

sequence :: Applicative f => [f a] -> f[a]
sequence = foldr (_hole (:)) (pure [])

and the question says: 问题说:

"What type is required for the function that needs to be placed at 
_hole in the following expression? Also give a definition for the 
expression using  and <$> and <*>".

I'm having issue understanding what the question is asking. 我在理解问题所在时遇到问题。 So for what I've tried, i assume that I'm required to specify the operator since it's using foldr so i assume its something like sequence = foldr((+) (:)) (pure[]). 因此,对于我尝试过的操作,我假设由于使用了foldr而需要指定运算符,因此我假设其类似于sequence = foldr((+)(:))(pure [])。

Then for the definition of the expression, i wrote something like: 然后为表达式的定义,我写了类似的东西:

sequence :: <*> f => [f a] -> f[a]
sequence = foldr <$> pure []

I'm pretty sure I'm not 100% correct so would appreciate some help on this on any corrections. 我很确定我不是100%正确的,因此希望在任何更正上对此有所帮助。

The exercise wants you to assume that some value _hole is defined somewhere, and that the code above type checks. 本练习希望您假设某个值_hole在某处定义,并且上面的代码进行检查。 Then, the goal is to determine what would be the type of that _hole . 然后,目标是确定_hole的类型。 Then, it would ask a possible definition for _hole . 然后,它将询问_hole的可能定义。

For instance, if we were given 例如,如果我们被给予

foo :: Int
foo = 3 + _hole

the answer should be _hole :: Int , since that's what we need to make the code above to work. 答案应该是_hole :: Int ,因为这是使上面的代码正常工作所需要的。 For the definition _hole = 2 is OK. 对于定义, _hole = 2是可以的。

Instead, in 相反,在

foo :: Int -> Bool
foo = const _hole "hello" True

then we need _hole :: Bool -> Int -> Bool and for instance _hole = \\bi -> b . 那么我们需要_hole :: Bool -> Int -> Bool ,例如_hole = \\bi -> b

Your own code is more complex, so it's better to write down all the steps: 您自己的代码更加复杂,因此最好写下所有步骤:

sequence :: Applicative f => [f a] -> f[a]
sequence = foldr (_hole (:)) (pure [])

Here foldr is used, which (on lists) has type 这里使用文件foldr ,(在列表中)具有类型

foldr :: (b -> c -> c) -> c -> [b] -> c

To type check, the arguments must have type 要输入检查,参数必须具有类型

_hole (:) :: b -> c -> c
pure [] :: c

the result of foldr , being called with only two arguments is 仅使用两个参数调用的foldr的结果是

sequence :: [b] -> c

since this must match the type of sequence above, we get 因为这必须与上面的sequence类型匹配,所以我们得到

[b] = [f a]
c = f [a]

Hence, b = fa and 因此, b = fa

_hole (:) :: f a -> f [a] -> f [a]
pure [] :: f [a]

The pure [] part type checks as it is. pure []零件类型将按原样检查。 For the other, we need 另一方面,我们需要

_hole :: (type of (:)) -> f a -> f [a] -> f [a]

ie since (:) :: d -> [d] -> [d] for any d , we get 即因为对于任何d (:) :: d -> [d] -> [d] ,我们得到

_hole :: (d -> [d] -> [d]) -> f a -> f [a] -> f [a]

where d can be picked arbitrarily. d可以任意选择。 It is "natural", though, to pick d=a , so that we get 但是,选择d=a是“自然的”,这样我们得到

_hole :: (a -> [a] -> [a]) -> f a -> f [a] -> f [a]

Now, we need to write a definition _hole fxy = ?? 现在,我们需要编写一个定义_hole fxy = ?? in terms of <$> and <*> . <$><*> Essentially, we need to re-implement liftA2 from the library. 本质上,我们需要从库中重新实现liftA2 You should now be able to solve this last part. 现在您应该可以解决最后一部分了。

Let's do it step by step, gradually discovering the types of the entities involved in our definition. 让我们一步一步地做,逐步发现我们定义中涉及的实体的类型。 We are given 我们被给予

sequence :: Applicative f => [f a] -> f [a]               --  (1)
sequence = foldr (_hole (:)) (pure [])                    --  (2)

so that sequence = mksequence g for some g : 因此对于某些gsequence = mksequence g g

mksequence g xs = foldr (g (:)) (pure []) xs              --  (3)

mksequence g [a,b,...,n] = r where                        --  (4)
    r = g (:) a $ g (:) b $ ... $ g (:) n (pure [])       --  (5)

mksequence g [a] = g (:) a (pure [])                      --  (6)

mksequence g []  = pure []                                --  (7)

-- [a,b,...,n] :: [f a]                     <-(4,1)       --  (8)
--  a,b,...,n  ::  f a                      <-(8)         --  (9)
--  r          :: f [a]                     <-(4,1)       --  (10)
-- pure []     :: f [a]                     <-(7,1)       --  (11)
-- g (:)       :: f a -> f [a] -> f [a]     <-(6,8,11,1)

Finally, we've found the type of g (:) ! 最后,我们找到了g (:)的类型! Compare it with 与它比较

(<*>)  :: f (a -> t) -> f a -> f t          , _A :: f (a -> t)
(_A <*> _C)                 :: f t          , _C :: f  a
(_B <*> _C)                 :: f (t -> s)   , _B :: f (a -> (t -> s))
((_B <*> _C) <*> _D)        :: f       s    , _D :: f        t

So that we have, 这样我们就可以了

\ _B _C _D -> ((_B <*> _C) <*> _D)  
          :: f (a -> (t  ->  s)) -> f a -> f  t  -> f  s
   g ((:) ::    a -> [a] -> [a]) :: f a -> f [a] -> f [a]

The signatures nearly match! 签名几乎匹配! With just a little nudge we have 只需轻轻一点,我们就可以

g (:) = (\ _B _C _D -> ((_B <*> _C) <*> _D)) (pure (:))

and so, generalizing, 因此,概括地说

g  f2  fa  ft  =  pure f2  <*>  fa  <*>  ft

because (<*>) associates to the left. 因为(<*>)关联到左侧。 Re-checking the types, 重新检查类型,

g  f2  fa  ft  =  pure f2  <*>  fa  <*>  ft
               =       f2  <$>  fa  <*>  ft

-- fa                 :: f a
-- f2                 ::   a -> t -> s
-- f2 <$> fa          :: f     (t -> s)
-- ft                 :: f      t
-- (f2 <$> fa) <*> ft :: f           s

In fact this definition already exists, and is named liftA2 -- for "lifting" a binary function ( f2 ) into an applicative "context" ( f ): 实际上,此定义已经存在,并被命名为liftA2用于将二进制函数( f2 )“提升”为适用的“上下文”( f ):

           f2 ::   a ->   t ->   s
    liftA2 f2 :: f a -> f t -> f s

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM