本文介绍了幻影类型背后的动机?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 Don Stewart's Haskell在Large 的演示中提到了 Phantom Types : 数据比率n =比率Double 1.234 ::比率D3 data询问ccy =询问Double 询问1.5123 ::询问GBP 我阅读了他的关于他们的重点,但我不明白他们。另外,我还阅读了有关该主题的 Haskell Wiki 。然而,我仍然错过了他们的观点。 使用幻影类型的动机是什么?解决方案回答使用幻像类型的动机是什么。有两点:$ b $ b 使无效状态不可表示,这在 Aadit的回答 提供类型级别的一些信息 例如,您可以使用长度单位标记距离: { - #LANGUAGE GeneralizedNewtypeDeriving# - } newtype距离a =距离Double 派生(Num,Show) 数据公里数据Mile marathonDistance ::距离公里 marathonDistance =距离42.195 distanceKmToMiles ::距离公里数 - >距离Mile distanceKmToMiles(Distance km)=距离(0.621371 * km) marathonDistanceInMiles ::距离Mile marathonDistanceInMiles = distanceKmToMiles marathonDistance 您可以避开火星气候轨道器灾难: >>> marathonDistanceInMiles 距离26.218749345 >>> marathonDistanceInMiles + marathonDistance < interactive>:10:27:无法将类型'Kilometer'与'Mile'匹配预期类型:距离Mile Actual类型:Distance Kilometer 在'(+)'的第二个参数中,即'marathonDistance'在表达式中:marathonDistanceInMiles + marathonDistance 这个模式有轻微的变化。您可以使用 DataKinds 来关闭一组单元: { - #LANGUAGE GeneralizedNewtypeDiving# $ b { - #LANGUAGE KindSignatures# - } { - #LANGUAGE DataKinds# - } 数据长度单位=公里数| Mile $ b $ newtype距离(a :: LengthUnit)=距离Double 派生(Num,Show) marathonDistance ::距离'公里 marathonDistance =距离42.195 距离KmToMiles ::距离'公里数 - >距离'Mile distanceKmToMiles(Distance km)=距离(0.621371 * km) marathonDistanceInMiles :: Distance'Mile marathonDistanceInMiles = distanceKmToMiles marathonDistance $ b> >>> marathonDistanceInMiles 距离26.218749345 >>> marathonDistance + marathonDistance 距离84.39 >>> marathonDistanceInMiles + marathonDistance < interactive>:28:27:无法将类型''公里数'与''Mile'匹配预期类型:Distance'Mile 实际类型:Distance'公里在'(+)'的第二个参数中,即'marathonDistance'在表达式中:marathonDistanceInMiles + marathonDistance 但现在距离只能以公里或英里计算,因此我们不能再增加更多单位。这可能在某些用例中很有用。 我们也可以这样做: data距离=距离{distanceUnit :: LengthUnit,distanceValue :: Double} 派生(显示) 在距离情况下,我们可以计算出加法,例如,如果涉及不同的单位,则转换为公里。但是这对于货币并不适用,该比率在一段时间内不是恒定的。 { - #LANGUAGE GeneralizedNewtypeDeriving # - } { - #LANGUAGE KindSignatures# - } { - #LANGUAGE DataKinds# - } { - #LANGUAGE GADTs# - } { - #LANGUAGE StandaloneDeriving# - } data公里数据Mile data距离a where KilometerDistance :: Double - >距离公里 MileDistance :: Double - >距离Mile 导出实例Show(Distance a) marathonDistance :: Distance Kilometer marathonDistance = KilometerDistance 42.195 distanceKmToMiles :: Distance公里 - >距离Mile distanceKmToMiles(KilometerDistance km)= MileDistance(0.621371 * km) marathonDistanceInMiles :: Distance Mile marathonDistanceInMiles = distanceKmToMiles marathonDistance 现在我们知道单位也是在价值层面上: >>> marathonDistanceInMiles MileDistance 26.218749345 特别是这种方法从 Aadit的回答中简化 Expr a 示例: { - #LANGUAGE GADTs# - } data Expr a where Number :: Int - > ; Expr Int Boolean :: Bool - > Expr Bool Increment :: Expr Int - > Expr Int Not :: Expr Bool - > Expr Bool 值得指出的是后者的变化需要非平凡的语言扩展( GADTs , DataKinds , KindSignatures ),这在编译器中可能不支持。这可能是 Mu编译器 Don提到的情况。 Don Stewart's Haskell in the Large's presentation mentioned Phantom Types:data Ratio n = Ratio Double1.234 :: Ratio D3data Ask ccy = Ask DoubleAsk 1.5123 :: Ask GBPI read over his bullet points about them, but I did not understand them. In addition, I read the Haskell Wiki on the topic. Yet I still am missing their point.What's the motivation to use a phantom type? 解决方案 To answer the "what's the motivation to use a phantom type". There is two points:to make invalid states inrepresentable, which is well explained in Aadit's answerCarry some of the information on the type levelFor example you could have distances tagged by the length unit:{-# LANGUAGE GeneralizedNewtypeDeriving #-}newtype Distance a = Distance Double deriving (Num, Show)data Kilometerdata MilemarathonDistance :: Distance KilometermarathonDistance = Distance 42.195distanceKmToMiles :: Distance Kilometer -> Distance MiledistanceKmToMiles (Distance km) = Distance (0.621371 * km)marathonDistanceInMiles :: Distance MilemarathonDistanceInMiles = distanceKmToMiles marathonDistanceAnd you can avoid Mars Climate Orbiter disaster:>>> marathonDistanceInMilesDistance 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 + marathonDistanceThere are slight varitions to this "pattern". You can use DataKinds to have closed set of units:{-# LANGUAGE GeneralizedNewtypeDeriving #-}{-# LANGUAGE KindSignatures #-}{-# LANGUAGE DataKinds #-}data LengthUnit = Kilometer | Milenewtype Distance (a :: LengthUnit) = Distance Double deriving (Num, Show)marathonDistance :: Distance 'KilometermarathonDistance = Distance 42.195distanceKmToMiles :: Distance 'Kilometer -> Distance 'MiledistanceKmToMiles (Distance km) = Distance (0.621371 * km)marathonDistanceInMiles :: Distance 'MilemarathonDistanceInMiles = distanceKmToMiles marathonDistanceAnd it will work similarly:>>> marathonDistanceInMilesDistance 26.218749345>>> marathonDistance + marathonDistanceDistance 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 + marathonDistanceBut now the Distance can be only in kilometers or miles, we can't add more units later. That might be useful in some use cases.We could also do:data Distance = Distance { distanceUnit :: LengthUnit, distanceValue :: Double } deriving (Show)In the distance case we can work out the addition, for example translate to kilometers if different units are involved. But this doesn't work well for currencies which ratio isn't constant over time etc.And it's possible to use GADTs for that instead, which may be simpler approach in some situations:{-# LANGUAGE GeneralizedNewtypeDeriving #-}{-# LANGUAGE KindSignatures #-}{-# LANGUAGE DataKinds #-}{-# LANGUAGE GADTs #-}{-# LANGUAGE StandaloneDeriving #-}data Kilometerdata Miledata Distance a where KilometerDistance :: Double -> Distance Kilometer MileDistance :: Double -> Distance Milederiving instance Show (Distance a)marathonDistance :: Distance KilometermarathonDistance = KilometerDistance 42.195distanceKmToMiles :: Distance Kilometer -> Distance MiledistanceKmToMiles (KilometerDistance km) = MileDistance (0.621371 * km)marathonDistanceInMiles :: Distance MilemarathonDistanceInMiles = distanceKmToMiles marathonDistanceNow we know the unit also on the value level:>>> marathonDistanceInMiles MileDistance 26.218749345This approach especially greately simplifies Expr a example from Aadit's answer:{-# LANGUAGE GADTs #-}data Expr a where Number :: Int -> Expr Int Boolean :: Bool -> Expr Bool Increment :: Expr Int -> Expr Int Not :: Expr Bool -> Expr BoolIt's worth pointing out that the latter variations require non-trivial language extensions (GADTs, DataKinds, KindSignatures), which might not be supported in your compiler. That's might be the case with Mu compiler Don mentions. 这篇关于幻影类型背后的动机?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 09-21 13:51