Haskell中的多态函数列表是什么?

4

Consider the code below:

t1 :: [Int] -> (Int,String)
t1 xs = (sum xs,show $ length xs)

t2 :: [Int] -> (Int,String)
t2 xs = (length xs, (\x -> '?') <$> xs)

t3 :: [Int] -> (Char,String)
t3 (x:xs) = ('Y',"1+" ++ (show $ length xs))
t3  []     = ('N',"empty")

这三个函数的类型只有部分变化——它们完全可以在不知道它们产生的元组的第一个组件类型的情况下使用。这意味着我可以在不需要引用该类型的情况下对它们进行操作:

fnListToStrs vs fs = (\x -> snd $ x vs) <$> fs

将这些定义加载到 GHCi 中后,这三个函数都可以独立地作为 fnListToStrs 的参数工作。实际上,我可以传入一个包含 t1 和 t2 的列表,因为它们具有相同的类型:

*Imprec> fnListToStrs [1,2] [t1,t2]
["2","??"]
*Imprec> fnListToStrs [1,2] [t3]
["1+1"]

但是我不能同时通过所有三个测试,尽管类型的分歧实际上与所执行的计算无关:

*Imprec> fnListToStrs [1,2] [t1,t2]
["2","??"]
*Imprec> fnListToStrs [1,2] [t3]
["1+1"]

我有一种感觉,让这个工作起来与存在类型或难以预测的类型有关,但是当使用我期望fnListToStrs能够接受的类型声明时,这两个扩展都没有起作用:

fnListToStrs :: [Int] -> [forall a.[Int]->(a,String)] -> [String]

有没有其他方法使这个工作?
2个回答

3
任何将这些函数放入列表的方法都需要以某种方式“包装”它们。最简单的包装方式就是:
wrap :: (a -> (b, c)) -> a -> c
wrap f = snd . f

确实,有其他的方法来包装这些(特别是存在类型),但是你没有提供任何信息表明其中任何一种在你的应用程序中比这个最简单的版本更好。

以下是一个需要更复杂的示例。假设你有:

data Blob a b = Blob [a -> b] [a]

现在想象一下,您要创建一个值类型为Blob a b的列表,这些值都具有相同的b类型,但可能具有不同的a类型。将每个函数应用于每个参数可能会导致潜在结果列表过大,因此编写以下代码是有意义的:

data WrapBlob b where
  WrapBlob :: Blob a b -> WrapBlob b

现在,您可以制作列表并推迟决定将哪些函数应用于哪些参数,而不必付出过高的代价。

3

正确的术语是存在类型,而不是内涵类型。Haskell没有存在类型,除非通过显式封装...

{-# LANGUAGE GADTs #-}

data SomeFstRes x z where
  SFR :: (x -> (y,z)) -> SomeFstRes x z

> fmap (\(SFR f) -> snd $ f [1,2]) [SFR t1, SFR t2, SFR t3]
["2","??","1+1"]

但是,这样做有点无用。因为你无法对第一个结果进行任何操作,所以更明智的做法是立即将其丢弃,并将剩余的函数放入简单的单态列表中:

> fmap ($[1,2]) [snd . t1, snd . t2, snd . t3]
["2","??","1+1"]

我在问题中可能过于简化了一些事情,这让我不确定如何实际应用。在我的实际场景中,函数已经被包装在一个类型中,但是两个结果类型都作为参数暴露给了该类型。我需要有效地创建一个忽略其中一个参数的列表,并且由于我正在尝试生成一个简化的API,我希望客户端不需要自己使用显式的包装器。但看起来这似乎不可能... - Jules

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接