将类型附加到函数

时间:2014-09-04 21:41:35

标签: haskell functional-programming

我得到了一个示意性定义的自定义数据类型:

data Foo = Foo
  { f1 :: Int
  , f2 :: b
  , f3 :: c
  }

我需要做什么

将一个字符串附加到Foo中记录的每个函数。在我看来,由于haskell多态性,这是微不足道的。


首先尝试:类

class Value a where
  val :: a -> String
class Query e where
  queries :: (Value a) => [(e -> a, String)]

instance Value Int where val = show
  instance Query Foo where
    queries =
      [ (f1, "function one")
      , (other records of Foo…)
      ]

哪个不起作用。我不确定我弄清楚原因。但我认为ghc期望类型(Foo - > a)的函数,但得到类型的函数(Foo - > Int)。 因此,多态性不适用于此。


第二次尝试:模式匹配

keyOf :: (Foo -> a) -> String
keyOf f1 = "function one"
keyOf f2 = "function two"
keyOf f3 = "function three"
keyOf _  = "unknown function"

我很满意它会编译。然后,在ghci:

λ keyOf f2 = "function one"

无法模式匹配函数名称,显然......


编辑:为什么我需要这样做

构造一个像这样的查询字符串:

"(keyOf f1)=(f1 Foo), (keyOf f2)=(f2 Foo), (keyOf f3)=(f3 Foo)"

更一般地说,用与之关联的字符串折叠Foo中记录的每个函数,结果就是这样。例如:

exampleFoo :: Foo
exampleFoo = Foo "one" "two" "three"

assocs = [(f1, "function one"), (f2, "function two"), (f3, "function three")]

result == "function one=one, function two=two, function three=three"

现在,我真的很想知道如果没有涉及haskell中的元编程(如TemplateHaskell),这种技巧是否可行。我没有考虑的任何选项? 感谢。

2 个答案:

答案 0 :(得分:2)

我认为您在这里寻找的是存在主义

如果你定义

data Selector e where
    Selector :: Value a => (e -> a) -> Selector e

(您需要文件顶部的{-# LANGUAGE GADTs #-}

然后,您可以将您的班级Query定义为:

class Query e where
    queries :: [(Selector e, String)]

instance Query Foo where
    queries =
        [ (Selector f1, "function one")
        , (other records of Foo…)
        ]

您之前定义的问题是queries的实现必须包含可以生成任何 a类型的函数。您想要的是queries的每个元素都会生成您选择的特定类型。这就是Selector类型的作用 - 它会隐藏该特定类型,以便您可以选择它而不是queries的调用者来选择。

在这个特定示例中,您可以使用a中的Selector类型进行唯一操作,将其转换为String Value,这样您就可以写得很好:

class Query e where
    queries :: [(e -> String, String)]

instance Query Foo where
    queries =
        [ (value . f1, "function one")
        , (other records of Foo…)
        ]

然而,如果Value是一个更复杂的类型类,特别是一个通常有用的类型类,那么这种扁平化会变得非常冗长。

答案 1 :(得分:0)

我认为主要的问题是你对data的工作方式感到困惑。

通过查看

keyOf :: (Foo -> a) -> String
keyOf f1 = "function one"
keyOf f2 = "function two"
keyOf f3 = "function three"
keyOf _  = "unknown function"

我想也许你的数据应该是这样的:

data Foo b c = F1 Int | F2 b | F3 c

这样,模式匹配将是:

keyOf :: Foo b c -> String
keyOf (F1 _) = "function one"
keyOf (F2 _) = "function two"
keyOf (F3 _) = "function three"

"unknown function"不是必需的,因为我们模式匹配每个可能的Foo构造函数。