4

所以最初我写道:

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

然后在我看来,函数'Just :: a -> Maybe a'有点“映射”在“提示”,“地址”和“端口”上,所以我想出了这样的东西:

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

但 GHC 期望 (ga)、(gb) 和 (gc) 属于同一类型,因此不会进行类型检查。

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

4

3 回答 3

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)

(for的实现Pointed就是MaybeJust想要的)

要获得通用版本,请注意

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和所有其他的只是简单的组合!

于 2013-10-24T05:07:26.473 回答
4

简短的回答是否定的(如果您谈论的是map_arg适用于任意数量参数的泛型)。

您也许可以使用 Oleg 级别的类型魔法来实现这一点,但如果您只是在寻找改进代码的方法,那么这里没有太多需要改进的地方。

于 2013-10-23T05:51:00.013 回答
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扩展来执行此操作。

于 2013-10-23T06:14:37.150 回答