如何在Haskell中编写人文单元作为后缀,如"3秒"?

nh2*_*nh2 29 haskell

Ruby有一个很好的功能,允许将数字转换为其他东西,例如3.times迭代或3.to_s将其转换为字符串.

人们说Haskell很适合编写自然DSL.

是否可以将单位作为后缀来编写,例如timeout = 3 seconds

nh2*_*nh2 74

是.

您可以使用以下简单方法执行此操作:

{-# LANGUAGE FlexibleInstances #-}

instance Num (Integer -> Integer) where
  fromInteger n = \scale -> n * scale  -- return a function that takes
                                       -- a number and returns a number
Run Code Online (Sandbox Code Playgroud)

然后你可以写:

seconds, minutes, hours, days :: Integer

seconds = 1000000 -- base unit, e.g. microseconds
minutes = 60 seconds
hours   = 60 minutes
days    = 24 hours

soon :: Integer
soon = 2 hours + 4 seconds
Run Code Online (Sandbox Code Playgroud)

这是如何运作的?

上面我们给出了一个Num实例Integer -> Integer,即一个取整数并返回一个整数函数.

实现NumfromInteger定义其函数的每个类型都允许用数字文字表示,例如3.

这意味着我们可以编写3 :: Integer -> Integer- 这3是一个采用整数并返回整数的函数!

因此,我们可以对它应用整数,例如seconds; 我们可以写3 seconds,表达式将是类型Integer.


更安全的版本

事实上,我们3 (3 :: Integer)现在甚至可以写作- 尽管这可能没什么意义.我们可以通过使其更加类型安全来限制它:

newtype TimeUnit = TimeUnit Integer
  deriving (Eq, Show, Num)

instance Num (TimeUnit -> TimeUnit) where
  fromInteger n = \(TimeUnit scale) -> TimeUnit (n * scale)

seconds, minutes, hours, days :: TimeUnit

seconds = TimeUnit 1000000
minutes = 60 seconds
hours   = 60 minutes
days    = 24 hours
Run Code Online (Sandbox Code Playgroud)

现在我们只能将类型的东西TimeUnit应用于数字文字.

你可以为各种其他单位做到这一点,比如重量或距离或人.

  • ......它既美丽又丑陋.恭喜.Upvoted. (20认同)
  • 为了得到合乎逻辑的结论,请参阅[unittyped](https://bitbucket.org/xnyhps/haskell-unittyped/)包.为你自动管理单元真的很棒,但它必须使用一堆扩展,错误消息是*荒谬*坏. (3认同)
  • 很高兴看到我要给出的答案.:) (2认同)
  • 这非常好,因为它允许我们放弃(*),但必须注意像'3 x ^ 2`这样的表达式.... (2认同)
  • ...因为`3`和`x`之间的函数应用比任何中缀运算符绑定得更紧,所以它不可能在没有括号的情况下乘以`3`之前对`x`求平方.这与您期望的方式相反. (2认同)