Jir*_*Jir 2 haskell types type-systems
继我的上一个问题之后,我问如何创建一个类型,将一个单元(例如Inch)建模为 Haskell 中的类型,我现在面临的问题是如何对该单元和其他单元执行操作并将它们混合在一起正确。
例如,给定:
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
import GHC.Generics
import Data.VectorSpace
newtype Inch = Inch Double
deriving (Generic, Show, AdditiveGroup, VectorSpace)
Run Code Online (Sandbox Code Playgroud)
如何定义一个函数来计算具有以下签名的面积?
circleArea :: Inch -> SquareInch
Run Code Online (Sandbox Code Playgroud)
那么“价格面积比”(例如,每英寸美元^2)怎么样?
priceAreaRatio :: Inch -> Price -> PricePerSquareInch
Run Code Online (Sandbox Code Playgroud)
这些签名看起来是错误的:我该如何表达SquareInch实际上是错误Inch * Inch的?那PricePerSquareInch真的是吗Price / (Inch * Inch)?
我在这里找到了一个潜在的解决方案,但我对 Haskell 不够精通,无法理解这是否只是一个玩具解决方案、一个实验,还是真正的一个好的实践。
我如何为我的问题建模?
首先,让我给出这样的观点:在我看来,类型应该被称为Length,而不是Inch。应该调用构造函数,Inches但用类型表示物理量的美妙之处在于该单位真正成为 \xe2\x80\x9cinvisible\xe2\x80\x9d 实现细节。事实上,您可以使用-XPatternSynonyms不同的构造函数来处理相同长度类型的不同单位,和/或用于单位转换的透镜同构。但这与问题有点相切。
这些评论已经链接到现有的物理单位库。对于实际项目来说,使用其中之一肯定是最明智的方法。
\n您链接的 Stephan Boyer 的博客文章绝对很有趣,但是用一般函数表示维商确实不太实用。
\n我将展示介于两者之间的一些内容:仍然使用定制类型而不是钟声物理单元类型,但无论如何都在更适合数字的框架内。
\n在您之前的问题中,我已经指出了向量空间库,因为VectorSpace(与Num) 是物理量的合适抽象。正如您所注意到的,它仅支持加法和按实数缩放,而不支持物理量的乘法或除法。
但向量空间的数学概念也适用于此类运算。Boyer 博客朝这个方向发展:它m/s用一个函数 Time -> Length来表示。这确实有意义:什么是速度?它告诉你,\xe2\x80\x9c,如果你等这么久,物体会移动\xe2\x80\x9d 多长时间。
然而,Time -> Length这是一种太大的类型,从某种意义上说,存储任意函数对于您知道也可以用单个数字表示的东西来说是完全矫枉过正且效率低下的,但更重要的是它不捕获基本思想:速度根据定义是一个线性化函数Time -> Length,因为对于足够小的时间增量,运动总是可以通过前两个泰勒项来近似。
众所周知,线性函数被合理地描述为矩阵\xe2\x80\xa0。在我们的例子中,时间和长度都是一维空间,所以它将是一个 1\xc3\x971 矩阵......再次是一个数字。
这种以类型安全的方式抽象线性函数但仍然以数字/矩阵作为内部表示的想法就是我编写 Linearmap -category 包的目的。它建立在vector-space,但类结果变得丑陋得多。幸运的是,对于像您这样的简单类型,实例可以自动生成:首先您用于-XGeneralizedNewtypeDeriving创建类的实例vector-space,然后有一个 Template Haskell 宏用于定义线性映射等类型。
{-# LANGUAGE TemplateHaskell, UndecidableInstances, GeneralizedNewtypeDeriving #-}\n\nimport Math.LinearMap.Category\nimport Math.LinearMap.Category.Instances.Deriving\nimport Data.VectorSpace\nimport Data.Basis\n\nnewtype Length = Inches Double deriving (Show, AdditiveGroup, VectorSpace, HasBasis)\nmakeLinearSpaceFromBasis [t| Length |]\n\nnewtype Price = Euros Double deriving (Show, AdditiveGroup, VectorSpace, HasBasis)\nmakeLinearSpaceFromBasis [t| Price |]\nRun Code Online (Sandbox Code Playgroud)\n现在您可以使用 中的类型组合器linearmap-category,并且始终立即对它们进行向量空间运算!正如我已经说过的,商对应于线性映射。产品对应于张量产品,这同样适用于一维情况,也只是围绕单个数字的新型包装。
type Area = Length \xe2\x8a\x97 Length\n\ntype PricePerArea = Area +> Price\n\ncircleArea :: Length -> Area\ncircleArea r = (4*pi)*^(r\xe2\x8a\x97r)\nRun Code Online (Sandbox Code Playgroud)\n我不太清楚你想要这个priceAreaRatio函数做什么,但这可能会用-+|>.