使用重载的数字和字符串文字键入类多态

时间:2018-04-03 23:13:48

标签: haskell polymorphism overloading typeclass

我正在尝试编写一些EDSL来为键分配值。所以我有以下数据类型的值:

data Value = B Bool | I Int

我希望有统一的方法将不同的值转换为Value类型的对象。所以我创建了以下类型类:

class    ToValue a    where toValue :: a -> Value
instance ToValue Bool where toValue = B
instance ToValue Int  where toValue = I

不幸的是,这段代码无法编译:

foo :: [Value]
foo = [toValue True, toValue 3]

我明白原因。但这让我感到难过。我真的不明白如何解决这个问题...如果我启用-XOverloadedStrings并且我想将T Text构造函数添加到我的Value类型,事情会变得更加困难。

我的最终目标是有能力写下这样的东西:

foo :: [(Text, Value)]
foo = [ "key1" !!! True
      , "key2" !!! 42
      , "key3" !!! "foo"
      , "key4" !!! [5, 7, 10]
      ]

据我所知,我总是可以手动将每个值包装到相应的构造函数中,但我更愿意避免这种情况(因为在我的实际生活中构造函数长于一个字母,并且代码并没有真正降低构造函数的噪声)。 / p>

我可以做些什么来实现最接近的可能实现?如果可能的话,我想避免Num的不安全Value实例。

1 个答案:

答案 0 :(得分:2)

使用ExtendedDefaultRules。 (它在GHCi中默认启用,并且pragma在GHC中启用它。)

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE OverloadedStrings #-}

default (Int, String)

class Value a where
  toValue :: a -> String

instance Value Int where
  toValue = show

instance Value String where
  toValue = id

main = do
  print (toValue 3)    -- would otherwise be ambiguous
  print (toValue "x")

旧答案

如果我理解正确,这里的目标是保持语法统一,同时适当地专门化文字。一种方法是使用Template Haskell,因此foo可能看起来像

foo = [$(toValue [|True|]), $(toValue [|3|])]

foo = [ [toValue| True |], [toValue| 3 |] ]  

后者的美元噪音较少,但实现自定义引用需要表达式解析器而 template-haskell 不提供。