【问题标题】:Pass Types as arguments to a function in Haskell?将类型作为参数传递给 Haskell 中的函数?
【发布时间】:2012-04-06 04:52:38
【问题描述】:

以下两个函数极其相似。它们从 [String] n 个元素中读取,[Int] 或 [Float]。我怎样才能把通用代码分解出来?我不知道 Haskell 中有任何机制支持将类型作为参数传递。

readInts n stream = foldl next ([], stream) [1..n]
  where
    next (lst, x:xs) _ = (lst ++ [v], xs)
      where
        v = read x :: Int

readFloats n stream = foldl next ([], stream) [1..n]
  where
    next (lst, x:xs) _ = (lst ++ [v], xs)
      where
        v = read x :: Float

我是 Haskell 的初学者,所以欢迎我的代码中的任何 cmets。

【问题讨论】:

  • 这里不用折,用一张简单的图就能搞定。例如map read stream :: [Int] 此外,您可能想了解为什么要在 Haskell 中使用 foldr 而不是 foldl。
  • @EdwardKmett 感谢您的建议。我真正想要的是只读取前 n 个元素,并返回列表和流的其余部分。我昨天超级困,想不通。我想您想说的是,使用 foldr 我可以直接使用构造函数:对吗?我后来改写成(map read firstn, rest) where (firstn, rest) = splitAt n stream,和你建议的很相似。
  • 你不需要嵌套where;您可以将next (lst, x:xs) _ = ...v = ... 放在连续的行中。

标签: haskell types


【解决方案1】:

Haskell 支持高度的多态性。特别是

readAny n stream = foldl next ([], stream) [1..n]
  where
    next (lst, x:xs) _ = (lst ++ [v], xs)
      where
        v = read x 

有类型

readAny :: (Enum b, Num b, Read a) => b -> [String] -> ([a], [String])

因此

readInts :: (Enum b, Num b) => b -> [String] -> ([Int], [String])
readInts = readAny

readFloats :: (Enum b, Num b) => b -> [String] -> ([Float], [String])
readFloats = readAny

您不需要专门化类型。 Haskell 会自动推断出最通用的类​​型,这里的 readAny 会做你想做的。

在 Haskell 中不能将类型作为参数传递。你很少需要。对于少数需要的情况,您可以通过传递所需类型的值来模拟行为。

Haskell 具有“返回类型多态性”,因此您真的不必担心“传递类型”——很可能函数会在您不告诉它们的情况下执行您想要的操作。

【讨论】:

  • 具有讽刺意味的是,传递类型以某种实现方式依赖于幕后发生的事情,因此问题并没有那么遥远......
  • 许多实现并没有传递类型,而是为类型类值传递类型一致的见证。像id 这样的东西根本不需要知道它的参数的类型(只要一切都是相同的大小)
  • 这就是我所说的依赖于实现的方式。但是我猜,没有任何约束的 id 根本没有通过。该类型保证 id 不会对它的参数做任何事情,这需要更多信息而不仅仅是“它是一些值”,但这应该是隐含的。
  • 对于任何可能会看到此问题的未来读者, 一种传递显式类型参数的方法。您可以设置-XTypeApplications 并使用read @T 请求将read 实例化为显式给定类型T。这也适用于 -XScopedTypeVariables-XPartialTypeSignatures
  • 可能是readInts :: Int -> [String] -> ([Int], [String])readFloats 类似)?
【解决方案2】:

基本上你想要的是不显式声明类型。相反,推迟声明类型并让推理引擎为您接管。另外,我认为您将折叠与地图混为一谈。这就是我的处理方式。

readList' :: Read a => [String] -> [a]
readList' = map read


ints = readList' ["1", "2"] :: [Int] -- [1, 2]

floats = readList' ["1.0", "2.0"] :: [Float] -- [1.0, 2.0]

要从流中只读取 n 个内容,请使用 take

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 2015-04-04
    • 2013-01-27
    • 2018-10-30
    • 1970-01-01
    • 2019-09-29
    相关资源
    最近更新 更多