Cześć szanownemu zgromadzeniu ludowemu
Znalazłem ciekawy artykuł-analizę na temat problemu ewaluacji wyrażeń arytmetycznych w językach C# i F#. Który język lepiej spełni swoją funkcję? Odpowiedź tutaj: http://www.neowin.net/forum/index.php?automodule=blog&blogid=460&showentry=3947
Taka ciekawostka do poczytania przy kawie. Enjoy :)
Dla porównania, podejście funkcyjne w Scali: Interpreter with continuations using monads
@Wibowit - Ty ze Scalą klasycznie, to ja może przedstawię wersję w Haskellu :P
data Exp = Op Exp (Int -> Int -> Int) Exp
| Literal Int
eval (Op a f b) = f (eval a) (eval b)
eval (Literal a) = a
4 linijki (coś pominąłem z funkcjonalności? W każdym razie poniższe działa).
Działa:
*Main> eval (Op
(Op (Literal 2) (+) (Literal 3))
(*)
(Op (Literal 4) (+) (Literal 5)))
45
Ale tak właściwie... W Haskellu da się lepiej - num jest jedną z typeclass (taki interfejs w Haskellu) więc można go spokojnie implementować:
data Func = Mul | Add | Sub deriving (Show, Eq)
data Num a => Exp a = Op (Exp a) Func (Exp a) | Lit a deriving (Show, Eq)
getFunc Add = (+)
getFunc Mul = (*)
getFunc Sub = (-)
instance (Num a) => Num (Exp a) where
a + b = Op a Add b
a - b = Op a Sub b
a * b = Op a Mul b
abs = undefined -- nie chce mi się
signum = undefined -- nie chce mi się
fromInteger = Lit . fromInteger
eval (Lit a) = a
eval (Op l f r) = (getFunc f) (eval l) (eval r)
Wygląda to o wiele ładniej:
*Main> (2 + 3) * (4 + 5) :: Exp Int
Op (Op (Lit 2) Add (Lit 3)) Mul (Op (Lit 4) Add (Lit 5))
*Main> eval ((2 + 3) * (4 + 5) :: Exp Int)
45
*Main> eval ((2 + 3) * (4 + 5))
45
Co to znaczy - że w tej chwili to, co wydaje się być zwykłym ciągiem operacji (np. 2 + 3) może zostać, w zależności od kontekstu automatycznie zmienione na naszą własną wewnętrzną strukturę i dowolnie modyfikowane...
IMO ciekawe jak w różnych dziwnych językach można implementować to samo zadanie. ;)
Haskell miażdży cyce jeśli chodzi o inferencję typów. Szkoda, że w Scali nie ma jej tak silnej. Jest w ogóle jakiś inny język z tak silną inferencją jak w Haskellu?