首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对Haskell型歧义现象的理解

对Haskell型歧义现象的理解
EN

Stack Overflow用户
提问于 2014-01-19 18:15:49
回答 2查看 192关注 0票数 5

我编写了一个Haskell程序,得到了一个我不明白的编译错误。

该方案应:

  • 获取命令行参数
  • 将令牌化参数串联回单个String
  • String读入NestedList数据类型
  • NestedList扁平化为List
  • 打印List

不幸的是,由于类型的歧义,它无法编译。

Haskell代码:

代码语言:javascript
复制
{-
  Run like this:
  $ ./prog List [Elem 1, List [Elem 2, List [Elem 3, Elem 4], Elem 5]]
  Output: [1,2,3,4,5]
-}
import System.Environment
import Data.List

data NestedList a = Elem a | List [NestedList a]
  deriving (Read)

main = do
  args <- getArgs
  print . flatten . read $ intercalate " " args

flatten :: NestedList a -> [a]
flatten (Elem x) = [x]
flatten (List x) = concatMap flatten x

编译错误:

代码语言:javascript
复制
prog.hs:8:21:
    Ambiguous type variable `a0' in the constraints:
      (Read a0) arising from a use of `read' at prog.hs:8:21-24
      (Show a0) arising from a use of `print' at prog.hs:8:3-7
    Probable fix: add a type signature that fixes these type variable(s)
    In the second argument of `(.)', namely `read'
    In the second argument of `(.)', namely `flatten . read'
    In the expression: print . flatten . read

有人能帮助我理解如何/为什么存在类型歧义,以及如何使代码明确无误吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-01-19 18:31:25

每当类型变量因函数应用程序而消失时,Haskell中就会出现模糊类型。你这里有一个,read/show是很常见的。问题是:

让我们尝试读取一个字符串,此操作具有以下类型

代码语言:javascript
复制
read :: Read a => String -> a

如果我们给它一个字符串,我们只会得到一个看起来像

代码语言:javascript
复制
read "()" :: Read a => a

换句话说,类型系统还不能选择一个具体的类型--它只是知道,不管答案是什么,它必须是Read能够的。

问题是,如果我们转过身,立即显示这一点,我们就会应用一个函数

代码语言:javascript
复制
show :: Show a => a -> String

它也没有完全指定a类型。把它们结合起来给我们

代码语言:javascript
复制
show (read "()") :: String

我们已经失去了所有的机会来决定这种中间类型应该是什么。

由于这种模糊性,Haskell不允许这样的表达。您可以通过插入一个完全约束类型的函数来修复它。一个常见的方法是使用函数asTypeOf

代码语言:javascript
复制
asTypeOf :: a -> a -> a
asTypeOf = const

它确保第一个和第二个参数具有相同的类型。

代码语言:javascript
复制
> show (read "()" `asTypeOf` ()) :: String
"()"

在您的特定示例中,您需要确定aNestedList a中的位置。这样做的一个简单方法是显式地将flatten类型作为具体类型。

代码语言:javascript
复制
print . (flatten :: NestedList Int -> [Int]) . read $ concat args
票数 7
EN

Stack Overflow用户

发布于 2014-01-19 18:47:45

这是个典型的问题。类型类的“即席”多态使得类型推断不完整,而且您刚刚被咬伤。让我们看看这些碎片。

代码语言:javascript
复制
read    :: Read x => String -> x
flatten :: NestedList a -> [a]
print   :: Show y => y -> IO ()

我们还将有机器生成的实例

代码语言:javascript
复制
Read a => Read (NestedList a)
Show a => Show (NestedList a)
Read a => Read [a]
Show a => Show [a]

现在,让我们来解决当我们试图构建合成时所得到的方程。

代码语言:javascript
复制
print   .     flatten              .   read

      y = [a]         NestedList a = x

这意味着我们需要

代码语言:javascript
复制
Show [a]                     Read (NestedList a)  

因此

代码语言:javascript
复制
Show a                       Read a

我们使用了所有的信息而没有确定a,因此使用了相关的ReadShow实例。

正如J.Abrahamson已经建议的那样,你需要做一些决定a的事情。做这件事有很多方法。我倾向于选择类型注释,而不是编写奇怪的术语,其唯一目的是使类型更加明显。我支持向组合中的一个组件提供一个类型的建议,但是我可能会选择(read :: String -> NestedList Int),因为这就是引入模糊类型的操作。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21220655

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档