我正在尝试实现“强类型”,即携带更多信息的新类型,如下面的代码片段所示
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
class UnderlyingTypeable a b where
get :: a -> b
newtype Voltage = Voltage Double deriving (Show,Read,Num, Eq,Ord)
instance UnderlyingTypeable Voltage Double where
get(Voltage v) = v
newtype Resistance = Resistance Double deriving (Show,Read,Num, Eq,Ord)
instance UnderlyingTypeable Resistance Double where
get(Resistance r) = r
newtype Ampere = Ampere Double deriving (Show,Read,Num, Eq,Ord)
instance UnderlyingTypeable Ampere Double where
get(Ampere a) = a
v1 = Voltage 15
v2 = Voltage 21
--works nicely.
sum = v1 + v2
r = Resistance 10
-- does not compile. Great.
--foo = v1/r
--Works ok. We got "strong type" that prevent adding cabbage and carot unless explicitly said..
i = Ampere $ (get v1) / (get r)一切正常。并且可以使用不同的基础类型。
现在我要努力概括一下:
如何定义get函数的“默认”实现?(以避免非常重复和枯燥的显式实例)
第二个问题(更理论性的)。我确实花了一些时间才发现我需要激活{-# LANGUAGE MultiParamTypeClasses #-}才能使类型类声明确定。是否有可安全激活的语言扩展列表?-安全地说,我的意思是它们不应该破坏代码,不使用它,它们不应该自己重叠。
发布于 2020-03-25 01:17:24
正如@chi在一条评论中指出的那样,GHC使用来自coerce的Data.Coerce函数,有一个“安全强制性”的概念。实际的规则有点复杂,但简化了一些,可以使用coerce在具有相同表示形式的两种类型之间进行转换,并且每个newtype具有与其底层类型相同的表示形式,因此可以安全地强制使用其基础类型(或从其底层类型):
i = Ampere $ coerce v1 / coerce r考虑到这一点,您甚至可能不需要您的UnderlyingTypeable类,并且可以直接使用coerce。但是,如果您仍然需要它,那么下面的内容应该提供一个合理的缺省值:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DefaultSignatures #-}
import Data.Coerce
class UnderlyingTypeable a b where
get :: a -> b
default get :: Coercible a b => a -> b
get = coerce为了回答你的第二个问题,FP Complete ( Stack的制造者等)提供了一份推荐的扩展列表,作为他们的RIO项目的一部分。见自述文件。他们之所以被选中,是因为他们被Haskell社区所接受,不太可能破坏任何东西,而且通常被认为是安全的。
发布于 2020-03-25 13:46:54
有许多包,如新型仿制药、矫顽力和透镜 ( Control.Lens.Wrapped模块),它们提供了一个api,用于处理新类型、统一包装器和解包器等特性。
例如,使用来自强制-utils的unpack:
{-# LANGUAGE DeriveGeneric #-}
import CoercibleUtils.Newtype
import GHC.Generics
newtype Voltage = Voltage Double deriving (Show,Read,Eq,Ord,Generic)
newtype Resistance = Resistance Double deriving (Show,Read,Eq,Ord,Generic)
i = Ampere $ unpack v1 / unpack runpack有一个Newtype约束。根据文件:
此模块导出的Newtype类的版本有一个实例,用于所有新类型,其泛型实例生成的强制实例是可见的。用户不需要也可能不应该编写自己的实例。
因此,似乎为我们的新类型派生Generic就足够了,并确保构造函数在作用域中(对于Coercible实例)。
https://stackoverflow.com/questions/60839394
复制相似问题