Haskell - 类型转换?

时间:2016-09-12 11:28:15

标签: haskell type-conversion

我尝试将列表中的数据' profile1'转换为名为' DataSubject'的自定义类型。

我将此传递给了一个函数' makeDS'试图进行这种转换 - 但是以下内容并不起作用:

type Name = String 
type Age = Int 
type Iq = Int
type Language = String 
data DataSubject = DS {name :: Name, age :: Age, iq :: Iq, language :: Language} deriving (Show)
data Contain = Name String | Age Int | Iq Int | Language String deriving (Show) --Use so list can take multiple types

profile1 = [Name "Bob", Age 22, Iq 100, Language "French"]

makeDS :: [Contain] -> DataSubject
makeDS t = DS {name = t!!0, age = t!!1, iq = t!!2, language = t!!3}

main = do
  let x = makeDS profile1
  putStrLn $ show x

错误:

Couldn't match type ‘Contain’ with ‘[Char]’

我刚刚开始使用Haskell - 有人可以就我的错误提出建议吗?如果有更好的方法可以做到这一点?

3 个答案:

答案 0 :(得分:7)

makeDS的定义中,变量t的类型为[Contain](即Contain的列表),所以当你说t!!0时将提取该列表的第一个元素,其类型为Contain。问题是name的{​​{1}}字段包含DataSubjectString的别名)。因此,您试图将[Char]存储在Contain的位置,这是不可能的,因为类型不同。您需要在代码中使用不同的方法。

一个问题是,每个[Char]值代表Contain的单个字段。因此,如果我们获得了DataSubject的列表,则无法保证将按特定顺序(例如Contain,然后是Name等)提供值,或者甚至是提供所有字段。即使您始终按照约定在代码中按特定顺序提供所有字段,haskell也不可能知道这一点。一个不依赖于订单的解决方案是尝试构建" Age对象一步一步地从"空"开始DataSubject然后检查DataSubject列表并添加相应的Contain字段:

DataSubject

所以在这里,我定义了makeDS :: [Contain] -> DataSubject makeDS = foldr updateDS emptyDS where updateDS (Name s) ds = ds {name = s} updateDS (Age n) ds = ds {age = n} updateDS (Iq n) ds = ds {iq = n} updateDS (Language s) ds = ds {language = s} emptyDS = DS {name = "", age = 0, iq = 0, language = ""} 这是一个"空" emptyDS对象和名为DataSubject的函数,它采用(单个)updateDSContain并根据{{1}指定的字段更新DataSubject然后它返回它。最后,我使用折叠运行使用DataSubject重复更新Contain(以DataSubject开头)。

答案 1 :(得分:3)

您有类型不匹配。您有Contain的列表。所以当你使用

t !! 0

您获得了Contain,而不是String,这是nameDS所必需的。您需要一个函数Contain -> Name,例如

containToName :: Contain -> Name
containToName (Name xs) = xs
containToName _         = error "not a name"

但是,这是一个部分功能,因为containToName (Age 12)会导致错误。

请注意,这与类型类无关。现在,如果你想使用profile1,一种方法就是使用

profile1 :: DataSubject

而不是

profile1 :: [Contain]

e.g。

profile1 :: DataSubject 
profile1 = DS "Bob" 22 100 "French"

毕竟, type [Contain]中没有任何内容可以确保您拥有完整DataSubject的所有要素。

  

如果有更好的方法可以做到这一点?

这取决于你想做什么。如果您只想处理DataSubject,请不要使用Contain的临时列表。如果你想处理用户输入(或类似的),它会变得有点棘手。

答案 2 :(得分:1)

DataSubject声明表示我们需要Name字段nameNameString相同。因此,在DS {name = t!!0, ...}表达式中,我们需要t !! 0来返回String。但是,t !! 0会返回t的元素,t的类型为[Contain]。因此t !! 0的类型为Contain,与String不同。

要修复此类型错误,您需要将Contain转换为String,可能是这样:

DS { name = case t !! 0 of Name s => s, ... }