【问题标题】:Extending `read`-like behavior扩展类似`read`的行为
【发布时间】:2015-12-04 17:11:44
【问题描述】:

我正在学习 Haskell,但遇到了一个我似乎无法解决的问题。基本上,我的用例如下。我正在处理一个字符串;如果它以" 字符开头,那么我想将它作为字符串返回(剥离");否则,我想在上面返回read 的结果。换句话说:

parse "\"foo\"" -> "foo"

parse "3" -> 3

parse "1.5" -> 1.5

到目前为止,我已经尝试了以下方法。

  1. 多态返回类型
parse :: String -> a
parse ('"':xs) = init xs -- strip closing '"'
parse string = read string

这会产生编译时错误Couldn't match expected type `a' with actual type `[Char]'a 不应该匹配任何类型,包括像 [Char] 这样的复杂类型?

  1. 类型类
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
class Parse where parse :: String -> a
instance Read a => Parse a where parse = read
instance Parse String where parse = init . tail

这会编译,但会出现以下运行时错误:

Overlapping instances for Parse a0 arising from a use of `parse'
Matching instances:
  instance Read a => Parse a -- Defined at parse.hs:5:14
  instance Parse [Char] -- Defined at parse.hs:7:14
(The choice depends on the instantiation of `a0'
 To pick the first instance above, use -XIncoherentInstances
 when compiling the other instance declarations)

String 不是Read 的实例,所以我不太确定它在哪里看到了重叠。

顺便说一句,编译指示在那里,否则我会得到编译时错误:

Illegal instance declaration for `Parse [Char]'
  (All instance types must be of the form (T a1 ... an)
   where a1 ... an are *distinct type variables*,
   and each type variable appears at most once in the instance head.
   Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Parse [Char]'

Constraint is no smaller than the instance head
  in the constraint: Read a
(Use -XUndecidableInstances to permit this)
In the instance declaration for `Parse a'
  1. 使String 成为Read 的实例

我不确定如何执行此操作,文档对我来说也不是很清楚。不过,如果我能弄清楚,听起来这可能是正确的做法。但是一个问题一直萦绕在我的脑海中:如果我在一个模块中将String 设为Read 的实例,那么即使我不导出相关位,这是否会更改整个应用程序的类?如果是这样,那么我不确定我是否喜欢它的含义。


这就是我到目前为止所尝试的。我的方法错了吗?基本上是正确的,我只需要修复几个细节吗?请告诉我。

【问题讨论】:

  • 看起来您想要一个依赖类型的函数,因为返回类型取决于其参数的值。你实际上想做什么,因为这通常意味着你做错了事?
  • @bheklilr:我正在尝试完全按照我所说的去做:将字符串解析为未知类型的值,这与read 函数所做的非常相似。为什么你认为这是错误的方式?如果是错误的方式,那么正确的方式是什么?
  • 你希望使用这个函数的代码是什么样的?调用这个函数后不知道发生了什么事情有用吗?用Either怎么样?
  • read 不会将值解析为未知类型。尝试在 GHCi 中输入read "1234"。它不起作用,是吗?现在输入read "1234" :: Int,这是有效的,因为您已经告诉read 要返回什么类型。并不是read 决定返回类型,必须在调用它之前从read 确定你想要的返回类型。如果您没有指定显式签名并且它仍然可以编译,这是因为 GHC 使用类型推断来确定应该去那里的类型,但归根结底read 并没有确定它的返回类型。
  • @MarnenLaibow-Koser read 仍然在编译时确定其类型。这就是为什么我说为了编译 GHC 将确定那里需要的类型,但它始终是调用者确定的多态性,而不是被调用者。

标签: haskell


【解决方案1】:

a 不应该匹配任何类型,包括像 [Char] 这样的复杂类型?

它可以而且确实如此。这里的问题是谁可以选择a 是什么。你写的类型是parse :: String -> a 的缩写

parse :: forall a. String -> a

你应该读为:“调用者选择一个类型a和一个Strings,而parse s产生一个a类型的值”。这里的重要部分是调用者而不是parse 选择了替换a 的类型。所以如果你写

parse s = ""

这是一个错误,因为调用者可能选择了String以外的类型!您还可以想象parse 可以选择的类型;这被称为存在类型,它对调用者如何使用产生的值有一些严格的限制。

String 不是Read 的实例,所以我不太确定它在哪里看到了重叠。

你错了:String Read 的一个实例。它使用这两个实例:

instance Read Char -- Defined in ‘GHC.Read’
instance Read a => Read [a] -- Defined in ‘GHC.Read’

(回想一下 type String = [Char]。)例如,在 ghci 中:

> read "\"foo\"" :: String
"foo"

也许这个实例对你的目的来说已经足够了!

【讨论】:

  • Srsly?我错过了String 作为Read 的一个实例?我查看了文档并没有找到它!这可以解释“重叠类型”错误。现在再次检查文档...
  • 次要命令点:read for String 还修改了 [Char]readListPrec 行为以允许读取字符串 ("foo") 和字符列表 (['f' ,'o','o'])。 ReadShow 都允许列表重载以允许像 String 这样的东西。字符串的Read 实例定义为here
  • 是的,那是我的问题:我不知道StringRead 的一个实例,所以我既要重新实现read,又要创建类型问题。谢谢。但是...我怎么知道StringRead 的一个实例?它似乎不在文档中。
  • @ReinHenrichs:那可能就是为什么我在文档中找不到任何关于 Read String 的答案。
  • @MarnenLaibow-Koser 您必须从CharReadtype String = [Char]instance Read a => Read [a] 的实例这一事实中推断出来。
猜你喜欢
  • 2019-01-31
  • 1970-01-01
  • 1970-01-01
  • 2013-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多