Vraag Hoe kan ik mens-taaleenheden schrijven als postfixes in Haskell, zoals `3 seconden`?


Ruby heeft een leuke functie waarmee je getallen naar andere dingen kunt converteren, bijvoorbeeld 3.times voor iteratie of 3.to_s voor het converteren naar een string.

Mensen zeggen dat Haskell goed is voor schrijven DSLs.

Is het mogelijk om eenheden te schrijven als postfixes, bijvoorbeeld timeout = 3 seconds?


27
2018-03-01 02:25


oorsprong


antwoorden:


Ja.

U kunt dit doen met de volgende eenvoudige truc:

{-# LANGUAGE FlexibleInstances #-}

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

Dan kun je schrijven:

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

Hoe werkt dit?

Hierboven hebben we een gegeven Num bijvoorbeeld voor Integer -> Integer, dat is voor een functie die een geheel getal vereist en een geheel getal retourneert.

Elk type dat implementeert Num en heeft zijn functie fromInteger gedefinieerd kan worden weergegeven door een numerieke letterlijke, b.v. 3.

Dit betekent dat we kunnen schrijven 3 :: Integer -> Integer - hier 3 is een functie die een geheel getal vereist en een geheel getal retourneert!

Daarom kunnen we er bijvoorbeeld een geheel getal op toepassen seconds; we kunnen schrijven 3 seconds en de uitdrukking zal van het type zijn Integer.


Een meer typevaste versie

Sterker nog, we zouden zelfs kunnen schrijven 3 (3 :: Integer) nu - dit heeft waarschijnlijk echter niet veel zin. We kunnen dit beperken door het type-veilig te maken:

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

Nu kunnen we alleen dingen van het type toepassen TimeUnit om literalen te nummeren.

Je zou dat kunnen doen voor allerlei andere eenheden, zoals gewichten of afstanden of mensen.


71
2018-03-01 02:25



Als uw eenheden in kleine letters zijn, kunt u het hierboven gegeven type TimeUnit gebruiken. Maar als uw eenheden in hoofdletters beginnen, moet u voor elke eenheid een nieuw type of nieuwe gegevens definiëren en de instantie Num voor hen definiëren. Een voorbeeld daarvan is in de Basic-interpreter
hier gedocumenteerd: http://hackage.haskell.org/package/BASIC-0.1.5.0/docs/Language-BASIC.html


0
2018-03-03 21:30