Haskell中用户定义的特定值类型?

R G*_*R G -4 haskell types

我已经用谷歌搜索了这个,但是却找不到任何相关的东西(让我感到惊讶,因为这似乎是许多人想要尝试的显而易见的东西).

我想做这样的事情:

data Car = "ford" | "chevy"
Run Code Online (Sandbox Code Playgroud)

换句话说,我希望右侧的值是特定的值,即特定的字符串,特定的数字等,而不是一般的东西.

这可能吗?我该怎么做?

谢谢.

编辑:我不是在找:数据车=福特| 烦扰

Mat*_*t S 8

data Car = Ford | Chevy

数据构造函数不是字符串.请注意,第一个字母的大小写不是可选的.在haskell中,数据构造函数必须将其首字母大写,并且值的标识符必须具有非首字母大写字母.

编辑:

这里是你如何能型的两个极限值CarFordChevy,仍然把它们作为字符串:

data Car = Ford | Chevy

carToString Ford = "Ford"
carToString Chevy = "Chevy"
Run Code Online (Sandbox Code Playgroud)

然后,您可以创建一个类似的值,mycar = Ford并且只要您想将值用作您只是使用的字符串carToString mycar.

  • 我相信我的编辑解释了如何获得该类型的安全性,并在必要时仍将值用作字符串. (4认同)
  • @MattS:如果你想要类型安全,请停止尝试使用字符串,而使用有意义的类型.我真的无法理解你为什么要首先将数据表示为字符串 - 这是一个糟糕的主意,正是因为你抱怨的原因! (4认同)
  • @RG:所以我假设你不在程序中使用数字类型?毕竟,"理想化类型",比如整数,不是用户在控制台输入的内容.这是否意味着类型必须归结为"4"或"11"等字符串而不是整数4或11? (2认同)

Ben*_*Ben 5

您正在尝试做的事情在Haskell中无法直接实现.data Car = "ford" | "chevy"看起来它正试图创建一个子类型String; 所有的Car值都是Strings,但不是所有的Strings都是Cars.您正在尝试的语法的更一般用法将创建奇怪的类型,这些类型是不受歧视的子类型联合(例如data Strange = 1 | "one" | '1'.

Haskell的类型系统没有子类型,所以这永远不会直接起作用.

如果你实际上并不关心类型的值CarStrings,那么你可以使用data Car = Ford | Chevy(如Matt S所提出的).

如果你确实关心类型的值CarStrings,那么这可能是因为你希望能够将函数String应用于Car值,但是你不希望能够将任何旧函数传递StringCar.

一种不同的方式来实现,从创建一个Car类型只有String你想s是创建一个Car类型加,让你的函数String对应于任何给定的Car.例如:

data Car = Ford | Chevy

carStr :: Car -> String
carStr Ford = "ford"
carStr Chevy = "chevy"
Run Code Online (Sandbox Code Playgroud)

然后你可以传递Car值并对Car它们进行操作,只要你真正需要这个值String(例如打印),你就可以调用carStr它.

然而Show,类型可以转换为a类String,您可以自动获取Show类似的实例:

data Car = Ford | Chevy
    deriving Show
Run Code Online (Sandbox Code Playgroud)

然后show函数将呈现Ford构造为String "Ford",和Chevy作为"Chevy".这不完全是你原来的,因为第一个字母是大写的(数据构造函数名称必须以大写字母开头,派生Show实例将匹配构造函数名称).您可以手动编写自己的Show实例而不是派生它,如下所示:

data Car = Ford | Chevy

instance Show Car where
    show Ford = "ford"
    show Chevy = "chevy"
Run Code Online (Sandbox Code Playgroud)

但是由于这么多Show实例为它们呈现的值产生了完全Haskell语法,我倾向于认为最好不要在可能的情况下脱离这个.但是你可以充分利用这两个世界,并拥有一个自动派生的Show实例,这样你就不必手动设置数据构造函数和它们产生的字符串之间的对应关系,再加上产生你想要的特定字符串的不同函数:

import Data.Char

data Car = Ford | Chevy
    deriving Show

carStr :: Car -> String
carStr = map toLower . show
Run Code Online (Sandbox Code Playgroud)


And*_*ewC 5

拥有字符串对你来说似乎非常重要,但你想要类型安全。字符串本质上不是类型安全的。抽象数据类型类型安全的,一旦你习惯了它,你就会发现你喜欢它。

好处之一是您可以允许用户使用任何大写字母(16 或 32 种可能性),但内部只有一种表示形式,因此一旦您读取了一次数据,使用数据就会快速高效。如果您在内部使用字符串,则每次您的程序取决于所使用的汽车时,您将永远进行更复杂的大写字符串检查。

如果您仅在内部使用`Car数据类型,则根本不需要字符串,因此使用字符串的唯一原因是处理输入和输出。这是一种具有类型安全性使用字符串处理输入/输出的方法。编译后,抽象数据类型具有非常小的数据表示形式,因此比字符串运行得更快。

data Car = Ford | Chevy
  deriving (Read,Show,Eq)
Run Code Online (Sandbox Code Playgroud)

现在我们可以读取、显示并检查是否相等。这使您有能力做到

read "Ford" -- gives Ford
show Chevy  -- gives "Chevy"
Ford == Chevy -- gives False
read "GM" -- gives an exception. oops.
Run Code Online (Sandbox Code Playgroud)

但也许对你来说比阅读和展示更有用的是

getCar :: String -> Maybe Car
getCar xs = case toLower xs of
   "ford"   -> Just Ford
    "chevy" -> Just Chevy
    _       -> Nothing
Run Code Online (Sandbox Code Playgroud)

这很棒,因为您可以轻松地使用它进行错误检查,而不会引发异常并使整个程序崩溃,并且您的用户可以使用他们想要的任何大小写。对于一个简单的例子,你可以写

feedback :: Maybe Car -> String
feedback Nothing = "Please enter a valid car. Why not use one of America's favourite brands? Ford or Chevy"
feedback (Just Ford) = "Thanks for choosing Ford."
feedback (Just Chevy) = "Thanks for choosing Chevy"
Run Code Online (Sandbox Code Playgroud)

你还需要

ungetCar :: Car -> String
ungetCar Ford  = "ford"     -- or you could use "Ford"
ungetCar Chevy = "chevy"    -- or you could use "Chevy"
Run Code Online (Sandbox Code Playgroud)

无论您使用"ford"还是"Ford"由您决定,但由于我没有在内部使用该字符串来检查输出,所以我也可以使用普通文本中的大写,"Ford"所以我可以写

ungetCar = show
Run Code Online (Sandbox Code Playgroud)

现在在您的代码中您可以编写

... if car == Ford then .... else .....
Run Code Online (Sandbox Code Playgroud)

代替

... if (map toLower carString) == "ford" then ... else
Run Code Online (Sandbox Code Playgroud)

类型安全的好处是,如果您的函数使用该Car数据类型,它可以毫无问题地假设用户已输入有效的汽车(福特或雪佛兰),并且不会因字符串不是“ford”而崩溃或出错或“雪佛兰”。这会将所有错误检查代码移至您第一次使用时getCar。如果您使用字符串,并且希望代码健壮,则每个使用汽车的函数都应该检查给定的汽车是否有效,如果不有效则抛出异常,然后您就浪费了大量处理器周期,并且您处理面条汤时出错。具有data Car = Ford | Chevy安全、快捷、方便的特点。

使用抽象数据类型允许您进行防御性编程,同时还可以检查一次是否存在无效数据。仅凭这一点,您就应该更喜欢它。它也清晰、干净、简单。

如果您稍后添加另一辆车

数据车 = 福特 | 雪佛兰| 通用汽车

您只需要更新几位代码(例如getCar)。