2016-01-03 8 views
0

Функция convert ниже имеет тип подписи:Простой в использовании температурный преобразователь в Haskell, возможно ли сократить этот код?

SUnit fromUnit-> SUnit toUnit ->Value fromUnit -> Value toUnit,

который имеет избыточность, так как та же информация может быть выражена:

Value fromUnit -> Value toUnit.

1) Есть ли способ избавиться от первых двух аргументов (SUnit fromUnit-> SUnit toUnit)?

2) Есть ли другой способ, с помощью которого эта простая навязчивая программа может быть написана более элегантно?

3) Как эта программа будет выглядеть в Идрисе?

{-# LANGUAGE GADTs,DataKinds,KindSignatures #-} 
main=do 
    putStrLn "Hello !" 
-- putStrLn $ show $ convert SCelsius SCelsius kelvinZero -- this line does not compile 
    putStrLn $ show $ convert SKelvin SKelvin kelvinZero -- prints Value 0.0 
    putStrLn $ show $ convert SKelvin SCelsius kelvinZero -- prints Value (-273.16) 

newtype Value (unit::Unit) = Value Double deriving Show 
data Unit = Celsius | Kelvin 

data SUnit u where 
    SCelsius:: SUnit Celsius 
    SKelvin:: SUnit Kelvin 

offset=273.16 
convert :: SUnit fromUnit-> SUnit toUnit ->Value fromUnit -> Value toUnit 
convert   SCelsius   SKelvin (Value tempCel) = Value $tempCel+offset 
convert   SCelsius   SCelsius (Value tempCel) = Value $tempCel 
convert   SKelvin   SCelsius (Value tempK) = Value $tempK-offset 
convert   SKelvin   SKelvin (Value tempK) = Value $tempK 

kelvinZero::(Value 'Kelvin) 
kelvinZero= Value 0 
+1

Ps, для тех, кто задается вопросом, как это может быть написано в Идриса книге Идриса, глава «3.4.3 Использование неявных аргументов в функции «Кажется, где ответ. – jhegedus

ответ

4

Если вы хотите удалить первые два аргумента, в Haskell вам понадобится класс.

class IsUnit a where 
    getSUnit :: SUnit a 
instance IsUnit Celsius where getSUnit = SCelsius 
instance IsUnit Kelvin where getSUnit = SKelvin 

convertShort :: (IsUnit fromUnit, IsUnit toUnit) => Value fromUnit -> Value toUnit 
convertShort = convert getSUnit getSUnit 

Обратите внимание, что это делает код больше, не меньше - но это позволяет вызывающим абонентам пропустить первые значения одноэлементных.

Приведенный выше код также предполагает, что каждый блок может быть конвертирован в любой другой, что нереально. Исходный код также содержит эту проблему. Если этого не хочет, то можно использовать два-параметров типа класса:

class C from to where convert :: Value from -> Value to 
instance C Kelvin Celsius where ... 
-- etc. 
+0

Это полезно, «это упрощает код вызывающего абонента. – jhegedus