如何定义一个允许统一访问Haskell中不同记录的类? [英] How to define a class that allows uniform access to different records in Haskell?
问题描述
我有两条记录,都有一个我想要提取的字段以供显示。我如何安排一些东西,以便可以使用相同的功能进行操作?由于他们有不同的字段(在这种情况下 firstName
和 buildingName
),它们都需要一些适配器代码来映射 firstName
到 name
。这是我到目前为止:
class Nameable a where
name :: a - >字符串
数据Human = Human {
firstName :: String
}
数据Building = Building {
buildingName :: String
实例可命名人类其中
名称x =名字x
实例可命名的建筑物其中
- 我认为x在这里是多余的,即以下应该工作:
- name = buildingName
name x = buildingName x
main :: IO()
main = do
putStr $ show(地图名称项目)
其中
items ::(Nameable a)=> [a]
items = [Human {firstName =Don}
- 理想情况下,我想要数组中的下一行,但是会给出
- 显而易见的类型错误时刻。
- ,Building {buildingName =Empire State}
]
这不会编译:
TypeTest.hs:23:14:
无法匹配预期的类型`a'反对推断类型`Human'
`a'是一个刚性类型变量,由
绑定,TypeTest.hs:22:23
处的`items'的类型签名在表达式Human {firstName =Don}
在表达式中:[Human {firstName =Don}]
在'items'的定义中:items = [Human {firstName =Don}]
我预计实例可命名人类
部分会使这项工作。有人可以解释我做错了什么,对于奖励点,我想要做什么概念,因为我无法知道要搜索什么。
这个问题感觉相似,但我无法弄清楚与我的问题。
考虑项目的类型
:
items ::(Nameable a)=> [a]
这就是说,对于任何可命名
type, items
会给我一个这种类型的列表。 是一个列表,可以包含不同的可命名的
类型,如你可能认为。你想要的东西像 items :: [exists a。可命名的a => a]
,除了你需要引入一个包装类型并使用 forall
来代替。 (请参阅:存在类型)
{ - #LANGUAGE ExistentialQuantification# - }
data SomeNameable = forall a。可命名的a => SomeNameable a
[...]
items :: [SomeNameable]
items = [SomeNameable $ Human {firstName =Don},
SomeNameable $ Building {buildingName =Empire State}]
数据构造函数中的量词 SomeNameable
基本上允许它忘记使用 a
的所有内容,除非它是命名
。因此,您只能使用元素中的 Nameable
类中的函数。
为了让这个更好你可以为这个包装器创建一个实例:
$ p $ instancename Nameable(SomeNameable a)其中
名称(SomeNameable x)= name x
现在你可以像这样使用它:
主要>地图名称项目
[Don,Empire State]
I have two records that both have a field I want to extract for display. How do I arrange things so they can be manipulated with the same functions? Since they have different fields (in this case firstName
and buildingName
) that are their name fields, they each need some "adapter" code to map firstName
to name
. Here is what I have so far:
class Nameable a where
name :: a -> String
data Human = Human {
firstName :: String
}
data Building = Building {
buildingName :: String
}
instance Nameable Human where
name x = firstName x
instance Nameable Building where
-- I think the x is redundant here, i.e the following should work:
-- name = buildingName
name x = buildingName x
main :: IO ()
main = do
putStr $ show (map name items)
where
items :: (Nameable a) => [a]
items = [ Human{firstName = "Don"}
-- Ideally I want the next line in the array too, but that gives an
-- obvious type error at the moment.
--, Building{buildingName = "Empire State"}
]
This does not compile:
TypeTest.hs:23:14:
Couldn't match expected type `a' against inferred type `Human'
`a' is a rigid type variable bound by
the type signature for `items' at TypeTest.hs:22:23
In the expression: Human {firstName = "Don"}
In the expression: [Human {firstName = "Don"}]
In the definition of `items': items = [Human {firstName = "Don"}]
I would have expected the instance Nameable Human
section would make this work. Can someone explain what I am doing wrong, and for bonus points what "concept" I am trying to get working, since I'm having trouble knowing what to search for.
This question feels similar, but I couldn't figure out the connection with my problem.
Consider the type of items
:
items :: (Nameable a) => [a]
It's saying that for any Nameable
type, items
will give me a list of that type. It does not say that items
is a list that may contain different Nameable
types, as you might think. You want something like items :: [exists a. Nameable a => a]
, except that you'll need to introduce a wrapper type and use forall
instead. (See: Existential type)
{-# LANGUAGE ExistentialQuantification #-}
data SomeNameable = forall a. Nameable a => SomeNameable a
[...]
items :: [SomeNameable]
items = [ SomeNameable $ Human {firstName = "Don"},
SomeNameable $ Building {buildingName = "Empire State"} ]
The quantifier in the data constructor of SomeNameable
basically allows it to forget everything about exactly which a
is used, except that it is Nameable
. Therefore, you will only be allowed to use functions from the Nameable
class on the elements.
To make this nicer to use, you can make an instance for the wrapper:
instance Nameable (SomeNameable a) where
name (SomeNameable x) = name x
Now you can use it like this:
Main> map name items
["Don", "Empire State"]
这篇关于如何定义一个允许统一访问Haskell中不同记录的类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!