让我们假设这两种总和类型
data Currency =
| GBP
| EUR
| DKK
data Country =
| DE
| AT
| DK
| UK
以及以下产品类型
type CC = (Country, Currency)
现在即使所有这些国家都是欧盟的一部分(是的,亲爱的3000年软件考古学家 - 英国曾经是欧盟的一部分;-))他们有不同的货币(或不是)。所以我想将CC
的可能值限制为
(DE, EUR)
(AT, EUR)
(UK, GBP)
(DK, DKK)
并使其他所有组合都不可表达
是否可以在类型级别上表达这样的东西?
如果不是那么精通Haskeller的方法怎么会这样呢?
答案 0 :(得分:8)
这可能有点过分,但根据你所处的背景,你可以使用GADT。这至少取决于您的至少您的货币没有任何构造函数信息。
{-# LANGUAGE GADTs, DataKinds #-}
data Currency = GBP | EUR | DKK
data Country c where
DE :: Country EUR
AT :: Country EUR
UK :: Country GBP
DK :: Country DKK
或者,我认为一个变体可能不太有用但更接近问题
{-# LANGUAGE GADTs, DataKinds #-}
data Currency = GBP | EUR | DKK
data Country = DE | AT | DK | UK
data CountryCurrency country currency where
DECC :: CountryCurrency DE EUR
ATCC :: CountryCurrency AT EUR
UKCC :: CountryCurrency UK GBP
DKCC :: CountryCurrency DK DKK
没有用例,很难说最好的方法是什么。 :)
答案 1 :(得分:4)
我的论点是产品类型是表示此映射的错误方式,并且这些类型不是检查其一致性的好工具。 (如何确保您在类型中获得了正确的映射?如果映射发生了变化,那么您也需要更改类型。)
每个国家/地区只有一种货币,货币由国家/地区唯一确定。听起来更像是一个功能,而不是一对。
currency :: Country -> Currency
currency DE = EUR
currency AT = EUR
currency UK = GBP
currency DK = DKK
答案 2 :(得分:1)
[编辑:我忽略了OP在类型级别上要求解决方案的事实,用于编译时检查;我的答案没有回答这个问题,但在类似情况下通常可以作为一个很好的选择]
我认为这样做的惯用方法是使用smart constructors。简而言之:您在模块中定义数据类型,而不导出“危险”数据构造函数(允许“非法”组合的构造函数);而是导出一个只生成合法值的函数。在你的情况下:
module Money ( CountryCurrency
, Country (..)
, Currency (..)
, cc
) where
data Currency = GBP | EUR | DKK deriving Show
data Country = DE | AT | DK | UK deriving Show
data CountryCurrency = CC Country Currency deriving Show
cc :: Country -> Currency -> CountryCurrency
cc DE EUR = CC DE EUR
cc AT EUR = CC AT EUR
cc UK GBP = CC UK GBP
cc DK DKK = CC DK DKK
cc _ _ = error "invalid country/currency combination"
cc
是智能构造函数。然后,您可以制作如下有效组合:
*Main> cc DE EUR
CC DE EUR
*Main> cc UK GBP
CC UK GBP
但你不能制造非法的:
*Main> cc UK EUR
*** Exception: invalid country/currency combination
CallStack (from HasCallStack):
error, called at ./Money.hs:18:10 in main:Money
最重要的是,您不能直接使用数据构造函数CC
:
*Main> CC UK EUR
<interactive>:26:1: error:
Data constructor not in scope: CC :: Country -> Currency -> t