【问题标题】:haskell: calling a function with different types of listshaskell:调用具有不同类型列表的函数
【发布时间】:2017-05-10 09:52:37
【问题描述】:

我有一个函数:

sum f l1 l2 = (f l1) + (f l2)

当使用不同类型的列表调用此函数时,如何更正此函数?例如:

sum length [1,2] ['a','b']

【问题讨论】:

  • 开启RankNTypes并添加以下类型签名:sum :: Num b => (forall a. [a] -> b) -> [c] -> [d] -> b。现在,sum length [1,2] ['a','b'] 返回2
  • @Alec 为什么是 2?不应该是4吗?
  • @AmiTavory 你打电话给putStrLn :: String -> IO ()而不是print :: Show a => a -> IO ()
  • @KlitosKyriacou 抱歉,是的。

标签: list haskell polymorphism


【解决方案1】:

不妨在答案中充实我的评论。一个人可能会想给出的通常签名是

sum :: Num b => ([a] -> b) -> [a] -> [a] -> b
sum f l1 l2 = f l1 + f l2

这里的问题是两个列表必须有相同的类型,必须是函数的输入类型。解决方案是告诉 GHC 该函数实际上具有更通用的类型 forall a. [a] -> b,这意味着我们可以选择多个可能不同 a 实例化,它们都产生相同的b

{-# LANGUAGE RankNTypes #-}

sum' :: Num b => (forall a. [a] -> b) -> [c] -> [d] -> b
sum' f l1 l2 = f l1 + f l2

main = print $ sum' length [1,2] ['a','b']

【讨论】:

  • @figasso 没问题。如果它解决了您的问题,请随时接受答案。
  • 值得注意的是,这通常不起作用,例如如果您的函数是map succ
  • @Clinton 你愿意详细说明succ 必须是什么吗?我不确定我是否理解...
  • 枚举成功,但它实际上不适用于您的情况,我将在下面的答案中详细说明。
【解决方案2】:

不幸的是,目前没有通用的方法来做到这一点。你可以试试这样previous answer建议如下:

sum' :: Num b => (forall a. [a] -> b) -> [c] -> [d] -> b
sum' f l1 l2 = f l1 + f l2

虽然这适用于 length,但它并不能真正适用于其他很多东西。

问题在于此答案中的类型签名Num b => forall a. [a] -> b。这意味着您的函数必须适用于所有类型的列表,而 Num b => forall a. [a] -> b 中唯一合理的函数是 length。如果您认为还有其他示例,请随意给我一个示例,但我怀疑所有其他示例要么是长度的变化,要么是返回常量的愚蠢示例。

如果lengthsum' 的唯一合理参数,那么定义sum' 是愚蠢的,你不妨像下面这样定义sumLength

sumLength :: Num b => [c] -> [d] -> b
sumLength l1 l2 = genericLength l1 + genericLength l2

确实,让我们定义以下内容:

g :: Enum a => [a] -> Int
g = (foldl' 0 (+)) . (map fromEnum)

这是一个奇怪的可能没用的函数,但它做了一些不平凡的事情。它将所有值转换为它们的 Enum int 表示形式并将它们相加并输出一个整数。

所以sum' g l1 l2 应该可以工作,但不能。要让它工作,你必须定义一个新函数:

sum'' :: Enum c, Enum d => (Enum a => forall a. [a]) -> [c] -> [d] -> Int
sum'' f l1 l2 = f l1 + f l2

事实上,使用任何具有不同约束的函数,您都必须定义一个新版本的sum

所以说真的,不,没有办法类似地回答你的问题。

我发现了这个问题并创建了包polydata,您可以在 hackage 上查看它(我承认需要一些更清晰的文档)。

它确实允许您制作接受多态函数的函数,您可以将这些函数应用于不同的类型,如下所示:

g :: (c (a -> a'), c (b -> b')) => Poly c -> (a, b) -> (a' -> b')
g f (x,y) = (getPoly f x, getPoly f y)

这与您的示例非常相似。

上面的c 是一个约束,查看g 的类型应该可以帮助您了解正在发生的事情。

不幸的是,您不能只将普通函数传递给g,您必须传递一个包裹在Poly 中的函数,这很重要,因为您无法获得Poly 约束的类型推断(任何关于如何使它变得更好的想法都值得赞赏)。

但是,如果您只有一个或几个需要这种多态行为的函数,我不会打扰Poly。但是例如,您发现这个问题出现了很多(我发现它在单元测试中出现了很多,这激发了我创建包的灵感),那么您可能会发现 polydata 很有用。

还有heterolist,我作为polydata 的扩展创建了它,它允许您创建混合类型的列表,并以类型安全的方式映射它们。您可能会发现这很有用。

【讨论】:

  • 常规length有效,因为b是调用者选择的,所以我们可以很好的选择Int
猜你喜欢
  • 2019-07-14
  • 1970-01-01
  • 2019-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-15
  • 2016-09-25
  • 1970-01-01
相关资源
最近更新 更多