Enr*_*lis 0 haskell functional-programming type-safety newtype
我使用的是 a Map String (Int, Int),其中两个Ints 用作分子和分母来形成Rational要传递给 的a fromList。
Int然后我意识到,在我的代码中的某个点上,我以相反的方式使用了这两个(作为分母和分子,即交换)。花了一些时间才找出问题所在,所以后来我想也许我应该使用两种专用类型,所以我写了
newtype MyNum = MyNum Int
newtype MyDen = MyDen Int
Run Code Online (Sandbox Code Playgroud)
但随后我必须添加一些实例才能使其他所有内容正常工作(考虑到我对这些Ints 的使用,我必须添加deriving (Eq, Ord, Show, Read)),并且还要添加一些两个函数来Int从两种类型中解开 s ,以便我可以实际上将类似的东西应用(+1)到那些包裹的Ints 上。
但这意味着代码开始看起来有点难看,比如(MyNum . (+1) . unwrapMyNum), 而类似的东西(+1) <$>会更好。
但这意味着MyNum应该是Functor;但它不能,因为它是硬类型,而不是类型构造函数。
但我不想将其设为类型构造函数,因为我不想在其中包含除Int.
有什么建议吗?
我认为实际问题与你的具体问题无关。只是不要使用 tuples,使用合适的类型来表达两个整数一起表示的内容。在这种情况下,显而易见的选择是使用Ratio Int,但需要注意的是它不存储任意对,而是正确标准化分数(这通常是一件好事)。如果这不适合您,请编写您自己的Ratio类型。
也就是说,您还可以做很多事情来使单一类型的新类型包装器更加方便:
派生实例。看来您仍在包装类型上使用数值运算。这很容易启用:
{-# LANGUAGE DerivingStrategies #-}
newtype MyNum = MyNum Int
deriving stock (Eq, Ord, Show, Read)
deriving newtype (Num, Enum, Real, Integral)
Run Code Online (Sandbox Code Playgroud)
现在MyNum基本上是功能齐全的克隆Int,可以直接编写negate (n + 9)for之类的表达式n :: MyNum,无需包装和展开。MyNum(但是当你将 a 传递给需要 a 的东西时,编译器仍然会犹豫MyDen。)
单函子。如果您想强调MyNum作为的容器Int,而不是其本身的不同类型的数字类型,那么就可以使用该类MonoFunctor。你可以实例化
{-# LANGUAGE TypeFamilies #-}
newtype MyNum = MyNum Int
type instance Element MyNum = Int
instance MonoFunctor MyNum where
omap f (MyNum i) = MyNum (f i)
Run Code Online (Sandbox Code Playgroud)
然后写eg omap (+1) n,它类似于(+1)<$>n但不要求容器支持除.之外的任何东西Int。还可以查看该包的其他类。
通用包装/展开助手。有一些替代方法可以显式操作新类型或其他包含的数据,这些方法在特殊类不适合时也可以工作,并且比传统的访问器和构造函数对更方便。其中包括coerce和镜头(更具体地说是Isos)。
| 归档时间: |
|
| 查看次数: |
110 次 |
| 最近记录: |