gpt4 book ai didi

haskell - 如何将 DU/ADT 限制为某些案例标识符/值构造函数

转载 作者:行者123 更新时间:2023-12-01 21:14:16 24 4
gpt4 key购买 nike

遇到以下情况我该如何处理?
我有一个 DU(例如货币)和一些记录类型。现在,对于记录类型字段,我要求给定实例的实际值应该具有相同的大小写标识符(或在 Haskell 中相同的值构造函数)

type Currency =
| USD of decimal
| EUR of decimal

type PositionalData = {
grossAmount: Currency;
pos1: Currency;
pos2: Currency;
}

例如以下内容是有效的

let valid = {
grossAmount = USD 10.0m;
pos1 = USD 7.0m;
pos2 = USD 3.0m;
}

这个例子应该是无效的

let wrong = {
grossAmount = USD 10.0m;
pos1 = USD 7.0m;
pos2 = EUR 3.0m;
^^^^^^^^^^^^^^^^
}

我知道可以使用测量单位在 F# 中解决这个特定示例。但很容易想象一个无法通过该机制解决的例子。因此,我想请您考虑一个更通用的答案,而不一定是仅解决给定代码示例的答案。

期待您的脑力转储;-)

PS:对于所有 Haskeleers 来说 - 看看 ADT(也许与更高种类的类型相结合)如何解决这个问题将会很有趣。

最佳答案

“直接”翻译可能是

{-# LANGUAGE GADTs, DataKinds, KindSignatures #-}

data Currency = USD | EUR deriving Show

-- We use `Currency` values to create `Amount` types
-- read about types in Haskell ([Kinds][1]: *, * -> *, ...)
-- here we fix one * to be Currency
data Amount :: Currency -> * where
-- Data constructor, take one float and return some Amount
Amount :: Float -> Amount a

-- Extract the specific currency symbol require extra effort
instance Show (Amount a) where
show (Amount k) = show k

-- Many amounts (same currency)
-- `a` restrict `a1` and `a1` to have the same type => the same currency
data PData a = PData { a1 :: Amount a
, a2 :: Amount a
} deriving Show

-- Helpers
usd :: Float -> Amount USD
usd = Amount

eur :: Float -> Amount EUR
eur = Amount

main = do

print $ PData (usd 3) (usd 4) -- OK
print $ PData (eur 3) (eur 4) -- OK
print $ PData (eur 3) (usd 4) -- KO, Couldn't match type 'USD with 'EUR

(1) https://wiki.haskell.org/Kind

另一方面,@TheInnerLight 记住我,你可以使用 phantom types

-- NOTE: this is not a "direct translation" since currencies are not
-- enumerated and is slightly different
data USD = USD
data EUR = EUR

data Amount c = Amount { amount :: Float }

instance Show (Amount c) where
show (Amount a) = show a

data PData c = PData { c1 :: Amount c
, c2 :: Amount c }
deriving Show

usd :: Float -> Amount USD
usd = Amount

eur :: Float -> Amount EUR
eur = Amount

main = do

print $ PData (usd 3) (usd 4) -- OK
print $ PData (eur 3) (eur 4) -- OK
print $ PData (eur 3) (usd 4) -- KO, Couldn't match type 'USD with 'EUR

提取货币符号(或任何其他数据)的一种方法可能是

class    Symbol c   where symbol :: c -> String
instance Symbol USD where symbol _ = "USD"
instance Symbol EUR where symbol _ = "EUR"

instance Symbol c => Show (Amount c) where
show s@(Amount a) = sym undefined s ++ " " ++ show a
where sym :: Symbol c => c -> Amount c -> String
sym k _ = symbol k

打印

PData {c1 = USD 3.0, c2 = USD 4.0}
PData {c1 = EUR 3.0, c2 = EUR 4.0}

关于haskell - 如何将 DU/ADT 限制为某些案例标识符/值构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38390690/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com