键入Family Type Hackery

时间:2012-04-08 00:46:22

标签: haskell types

我正在尝试编写一个相当多态的库。我遇到了一个比告诉更容易展示的情况。看起来有点像这样:

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Map (Map)
import qualified Data.Map as Map

class Format f where type Target f
class Format f => Formatter x f where
  target :: forall y. Formatable y => Target f -> x -> y
class Formatable y where name :: y -> String

instance Formatable Integer where name = show
instance Formatable Int where name = show

split :: forall x f. (Format f, Formatter x f) => x -> f -> String -> [Either String (Target f)]
split = undefined

display :: forall x f. (Format f, Formatter x f) => f -> String -> x -> String
display f str x = let
  chunks = split x f str
  built = foldr apply "" chunks
  apply (Left s) accum = accum ++ s
  apply (Right t) accum = accum ++ name (target t x)
  in foldr apply "" chunks

基本上,我们有多态Format s,它定义了许多Target s。还有一些Formattable个对象,它们知道如何响应一系列不同的格式选项(此处简化为name)。

这些Formattables以各种方式组成,可以响应许多不同的目标。 Formatter本质上是FormatFormattable之间的路由器 - 给定目标(来自特定格式),它们会使用合适的Formattable对象进行响应。

这一切都很抽象。这是一个例子:

  • DateFormat指定YearMonthDay等目标。
  • MonthTypeFormattable Int的新类型,其名称如“二月”
  • 还有简单的instance Formattable Int where name = show
  • DateTime可能是(Int, MonthType, Int)的类型同义词。

(显然,我已经在这里删除了很多机器,例如管道正确的值,但你明白了。)

display功能非常简单。它需要格式化程序,指定格式的字符串,要显示的对象,并将其全部呈现为字符串。

首先,它将字符串分解为目标和字符串。例如,日期格式化程序可能会将字符串"%Y-%m-%d"分解为[Right Year, Left "-", Right Month, Left "-", Right Day]split函数执行此操作,并已在此处进行了编辑。

display函数只是跟踪每个目标的Formattable并累积字符串。

或者,至少,它应该是。

但它未能通过以下错误进行类型检查:

Reduced.hs:20:16:
    Could not deduce (Target f ~ Target f0)
    from the context (Format f, Formatter x f)
      bound by the type signature for
                 display :: (Format f, Formatter x f) => f -> String -> x -> String
      at Reduced.hs:(19,5)-(24,30)
    NB: `Target' is a type function, and may not be injective
    Expected type: [Either [Char] (Target f0)]
      Actual type: [Either String (Target f)]
    In the return type of a call of `split'
    In the expression: split x f str
    In an equation for `chunks': chunks = split x f str
Failed, modules loaded: none.

我无法为我的生活找出原因。我做错了什么?

1 个答案:

答案 0 :(得分:2)

问题是Target f无法确定f,这意味着该功能

target :: (Formatter f x, Formatable y) => Target f -> x -> y

永远不会被召唤。无论你给target提供什么类型的注释,你都无法确定f是什么,因此编译器永远无法弄清楚要使用哪个Formatter实例。我不是百分百肯定,但可能解决方案不是使用多参数类型类,而是让xf中的一个成为另一个的函数。另外,你应该完全删除Format类(你知道你不需要一个类来使用类型族吗?)。也许是这样的:

class Formatter x where
    type Format x
    target :: Formatable y => Format x -> x -> y