如何在Haskell中的另一个函数的参数上映射函数?

时间:2013-10-23 05:37:17

标签: haskell

所以最初我写道:

xs <- getAddrInfo (Just hints) (Just addr) (Just port)

然后在我看来,函数'Just :: a - &gt;也许'有点'映射“超过'提示','地址'和'端口',所以我想出了类似的东西:

map_arg g f a b c = f (g a) (g b) (g c)
xs <- map_arg Just getAddrInfo hints addr port

但GHC期望(g a),(g b)和(g c)属于同一类型,因此不进行类型检查。

有没有办法做到这一点,或者更一般地说,是否有办法将函数映射到另一个函数的参数上?

3 个答案:

答案 0 :(得分:5)

最通用的类​​型签名看起来像

map_arg :: (forall b.b -> a b) -> (a b -> a c -> a d -> e) -> b -> c -> d -> e
map_arg g f a b c = f (g a) (g b) (g c)

对于您的情况,如果您选择不将g作为参数,则可以执行

map_just f a b c = f (g a) (g b) (g c) where g = Just
xs <- map_just getAddrInfo hints addr port

或者您只能将类型签名提供给g

map_arg (g :: forall b.b -> a b) f a b c = f (g a) (g b) (g c)

要绕过多态类型签名,请记住我们有Control.Functor.Pointed,因此您可以使用它:

map_arg :: Pointed p => (p a -> p b -> p c -> d) -> a -> b -> c -> d
map_arg f a b c = f (point a) (point b) (point c)

Pointed Maybe的实施Just是您想要的)

要获得通用版本,请注意

map1 :: Pointed p => (p a -> b) -> a -> b
map1 f = f . point

map2 :: Pointed p => (p a -> p b -> c) -> a -> b -> c
map2 f = map1 . map1 f

map3 :: Pointed p => (p a -> p b -> p c -> d) -> a -> b -> c -> d
map3 f = map2 . map1 f

看到了吗?你只需要map1而其他所有人只是简单的组合!

答案 1 :(得分:4)

简短的回答是否定的(如果你在讨论一个适用于任意数量参数的通用map_arg)。

您可以使用Oleg级别的魔术来实现这一目标,但如果您只是在寻找改进代码的方法,那么这里的改进并不多。

答案 2 :(得分:2)

只需向map_arg添加类型注释,并使g多态。

map_arg :: (forall a. a -> Maybe a) ->
           (Maybe AddrInfo -> Maybe HostName -> Maybe ServiceName -> IO [AddrInfo]) ->
           AddrInfo -> HostName -> ServiceName -> IO [AddrInfo]
map_arg g f a b c = f (g a) (g b) (g c)

您需要Rank2TypesRankNTypes分机才能执行此操作。