繁体   English   中英

在 Haskell 中对单位(例如英寸、美元等)的组成进行建模

[英]Modelling the composition of units (e.g. Inch, Dollar, etc) in Haskell

继我的上一个问题之后,我问我如何创建一个类型,它将 model 一个单位(例如Inch )作为 Haskell 中的一种类型,我现在面临如何对该单位和其他单位执行操作并混合的问题他们正确。

例如,给定:

{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}

import GHC.Generics
import Data.VectorSpace

newtype Inch = Inch Double
  deriving (Generic, Show, AdditiveGroup, VectorSpace)

如何定义 function 来计算具有以下签名的区域?

circleArea :: Inch -> SquareInch

那么“价格面积比”(例如,每英寸美元^2)呢?

priceAreaRatio :: Inch -> Price -> PricePerSquareInch

这些签名似乎是错误的:我如何表达SquareInch实际上是Inch * Inch 并且PricePerSquareInch真的是Price / (Inch * Inch)吗?

我在这里找到了一个潜在的解决方案,但我对 Haskell 的了解还不够,无法理解这只是一个玩具解决方案、一个实验还是一个很好的实践。

我怎么能 model 我的问题?

首先,让我给出这个意见:IMO 类型应该称为Length ,而不是Inch 构造函数应该称为Inches ,但用类型表示物理量的美妙之处在于,单位真的变成了“不可见”的实现细节。 实际上,您可以使用-XPatternSynonyms为相同长度类型的不同单位使用不同的构造函数,和/或使用镜头同构进行单位转换。 但这与问题有些相干。

评论已经链接到现有的物理单元库。 对于实际项目,使用其中一种绝对是最明智的方法。

您链接的 Stephan Boyer 的博客文章绝对很有趣,但是用通用函数表示维度商确实不是很实用。

我将在两者之间展示一些东西:仍然使用定制类型而不是 bell&whistley 物理单元类型,但无论如何在一个更适合数字的框架内。

在您之前的问题中,我已经指出了vector-space library ,因为VectorSpace (与Num不同)是物理量的合适抽象。 正如您所注意到的,它只支持按实数进行加法和缩放,而不支持物理量的乘法或除法。

但是向量空间的数学概念也确实扩展到了这样的操作。 Boyer 博客朝这个方向发展:它用function Time -> Length表示m/s 哪个有意义:什么是速度? 它告诉你,“如果你等那么久,object 会行驶多久”。

但是, Time -> Length是一种太大的类型,无论是从某种意义上来说,存储任意 function 对于您知道也可以用单个数字表示的东西来说都是过度杀伤和低效的,但更重要的是它也没有t 捕捉基本思想:根据定义,速度是线性化的 function Time -> Length ,因为对于足够小的时间增量,运动总是可以用前两个泰勒项来近似。
众所周知,线性函数被明智地描述为矩阵 在我们的例子中,时间和长度都是一维空间,所以它将是一个 1×1 矩阵......再次输入一个数字。

这种以类型安全的方式抽象线性函数但仍然使用数字/矩阵作为内部表示的想法是我编写线性映射类别 package的目的。 它建立在vector-space之上,但这些类变得更加丑陋。 幸运的是,对于像您这样的简单类型,可以自动生成实例:首先您使用-XGeneralizedNewtypeDeriving来制作vector-space类的实例,然后还有一个模板 Haskell 宏也用于定义线性映射等类型。

{-# LANGUAGE TemplateHaskell, UndecidableInstances, GeneralizedNewtypeDeriving #-}

import Math.LinearMap.Category
import Math.LinearMap.Category.Instances.Deriving
import Data.VectorSpace
import Data.Basis

newtype Length = Inches Double deriving (Show, AdditiveGroup, VectorSpace, HasBasis)
makeLinearSpaceFromBasis [t| Length |]

newtype Price = Euros Double deriving (Show, AdditiveGroup, VectorSpace, HasBasis)
makeLinearSpaceFromBasis [t| Price |]

现在您可以使用来自linearmap-category的类型组合器,并且总是立即对它们进行向量空间操作,正如我已经说过的。 商对应于线性映射。 乘积对应于张量乘积,这同样适用于一维情况,也只是围绕单个数字的新型包装器。

type Area = Length ⊗ Length

type PricePerArea = Area +> Price

circleArea :: Length -> Area
circleArea r = (4*pi)*^(r⊗r)

我不太清楚你想要priceAreaRatio function 做什么,但这可能会用-+|>来实现。


实际上,矩阵通常也不是一个好的表示,因为它们在空间的维度上是二次缩放的。 当然,这与这里无关。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM