gpt4 book ai didi

haskell - 幻象类型背后的动机?

转载 作者:行者123 更新时间:2023-12-03 06:22:31 25 4
gpt4 key购买 nike

唐·斯图尔特的Haskell in the Large的演示中提到了幻影类型:

data Ratio n = Ratio Double
1.234 :: Ratio D3

data Ask ccy = Ask Double
Ask 1.5123 :: Ask GBP

我读了他的要点,但我不明白。另外,我读了Haskell Wiki关于这个话题。但我仍然没有明白他们的观点。

使用幻像类型的动机是什么?

最佳答案

回答“使用幻像类型的动机是什么”。有两点:

  • 使无效状态无法表示,这在Aadit's answer中有很好的解释。
  • 携带一些类型级别的信息

例如,您可以使用长度单位标记距离:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Distance a = Distance Double
deriving (Num, Show)

data Kilometer
data Mile

marathonDistance :: Distance Kilometer
marathonDistance = Distance 42.195

distanceKmToMiles :: Distance Kilometer -> Distance Mile
distanceKmToMiles (Distance km) = Distance (0.621371 * km)

marathonDistanceInMiles :: Distance Mile
marathonDistanceInMiles = distanceKmToMiles marathonDistance

你可以避免Mars Climate Orbiter disaster :

>>> marathonDistanceInMiles
Distance 26.218749345

>>> marathonDistanceInMiles + marathonDistance

<interactive>:10:27:
Couldn't match type ‘Kilometer’ with ‘Mile’
Expected type: Distance Mile
Actual type: Distance Kilometer
In the second argument of ‘(+)’, namely ‘marathonDistance’
In the expression: marathonDistanceInMiles + marathonDistance
<小时/>

这个“模式”有轻微的变化。您可以使用DataKinds来拥有封闭的单位集:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}

data LengthUnit = Kilometer | Mile

newtype Distance (a :: LengthUnit) = Distance Double
deriving (Num, Show)

marathonDistance :: Distance 'Kilometer
marathonDistance = Distance 42.195

distanceKmToMiles :: Distance 'Kilometer -> Distance 'Mile
distanceKmToMiles (Distance km) = Distance (0.621371 * km)

marathonDistanceInMiles :: Distance 'Mile
marathonDistanceInMiles = distanceKmToMiles marathonDistance

它的工作原理类似:

>>> marathonDistanceInMiles
Distance 26.218749345

>>> marathonDistance + marathonDistance
Distance 84.39

>>> marathonDistanceInMiles + marathonDistance

<interactive>:28:27:
Couldn't match type ‘'Kilometer’ with ‘'Mile’
Expected type: Distance 'Mile
Actual type: Distance 'Kilometer
In the second argument of ‘(+)’, namely ‘marathonDistance’
In the expression: marathonDistanceInMiles + marathonDistance

但是现在距离只能以公里或英里为单位,以后我们无法添加更多单位。这在某些用例中可能很有用。

<小时/>

我们还可以这样做:

data Distance = Distance { distanceUnit :: LengthUnit, distanceValue :: Double }
deriving (Show)

在距离情况下,我们可以计算出加法,例如,如果涉及不同的单位,则转换为公里。但这对于货币来说效果不佳,因为货币的比率随着时间的推移并不是恒定的等。

<小时/>

并且可以使用 GADT 来代替,这在某些情况下可能是更简单的方法:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}

data Kilometer
data Mile

data Distance a where
KilometerDistance :: Double -> Distance Kilometer
MileDistance :: Double -> Distance Mile

deriving instance Show (Distance a)

marathonDistance :: Distance Kilometer
marathonDistance = KilometerDistance 42.195

distanceKmToMiles :: Distance Kilometer -> Distance Mile
distanceKmToMiles (KilometerDistance km) = MileDistance (0.621371 * km)

marathonDistanceInMiles :: Distance Mile
marathonDistanceInMiles = distanceKmToMiles marathonDistance

现在我们知道了值(value)层面上的单位:

>>> marathonDistanceInMiles 
MileDistance 26.218749345
<小时/>

这种方法极大地简化了 Aadit's answer 中的 Expr 示例。 :

{-# LANGUAGE GADTs #-}

data Expr a where
Number :: Int -> Expr Int
Boolean :: Bool -> Expr Bool
Increment :: Expr Int -> Expr Int
Not :: Expr Bool -> Expr Bool
<小时/>

值得指出的是,后面的变体需要不平凡的语言扩展(GADTsDataKindsKindSignatures),这可能不是您的编译器支持。 Don 提到的 Mu 编译器 可能就是这种情况。

关于haskell - 幻象类型背后的动机?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28247543/

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