扩展“读取”式行为

时间:2015-12-04 17:11:44

标签: haskell

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

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

parse "3" -> 3

parse "1.5" -> 1.5

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

  1. 多态返回类型
  2. 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. 类型类
    2. {-# 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的实例,所以我不确定它在何处看到重叠。

      顺便说一句,pragma是存在的,因为否则我会遇到编译时错误:

      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
      2. 的实例

        我不知道该怎么做,文档对我来说并不是那么清楚。但是,如果我能弄明白的话,听起来这可能是正确的做法。但是有一个问题在我脑海中浮现:如果在一个模块中我将String设为Read的实例,即使我不导出相关位,它是否会改变整个应用程序的类?如果可以,那么我不确定我是否喜欢它的含义。

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

1 个答案:

答案 0 :(得分:7)

  

不应a匹配任何类型,包括[Char]等复杂类型?

它可以做到。这里的问题是谁可以选择a是什么。您编写的类型是parse :: String -> a,它是

的缩写
parse :: forall a. String -> a

您应该将其读作:“来电者选择类型aString s,而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"

也许这个实例足以满足您的目的!