如何定义一个允许统一访问Haskell中不同记录的类? [英] How to define a class that allows uniform access to different records in Haskell?

查看:70
本文介绍了如何定义一个允许统一访问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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆