Curryfikované funkce v Haskellu

BoneFlute

  • *****
  • 1 987
    • Zobrazit profil
Curryfikované funkce v Haskellu
« kdy: 01. 05. 2018, 00:44:27 »
Zdravím.

Už u několika tutoriálů k minimálně dvoum jazykům se mi stalo, že tam autor zdůrazňoval skutečnost, že funkce
Kód: [Vybrat]
multThree 3 5 9 se převede na
Kód: [Vybrat]
((multThree 3) 5) 9.

Jako já s tím nemám problém. Curryfikování je fajn věc, a umím to použít. Ale proč je to zdůrazňováno, že "Každá funkce v Haskellu bere oficiálně pouze jeden parametr."? Bych to bral jako implementační detail a basta. Škoda slov, ne?

Nebo to má nějaký zajímavý efekt, který mi uniká?
« Poslední změna: 01. 05. 2018, 06:51:10 od Petr Krčmář »


Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:curryfikované funkce
« Odpověď #1 kdy: 01. 05. 2018, 01:23:47 »
Jo, je to detail, v podstatě se tím jen lidsky říká, že typový systém je kartézsky uzavřený, tj. v jistém smyslu úplný (má součinové, součtové a exponenciální typy).

JSH

Re:Curryfikované funkce v Haskellu
« Odpověď #2 kdy: 01. 05. 2018, 09:22:47 »
Nebo to má nějaký zajímavý efekt, který mi uniká?
Důležitý rozdíl je třeba ten, že nemusíš předat všechny parametry funkci najednou. Můžeš třeba navázáním jedničky udělat ze součtu inkrement a ten pak jako funkci s jedním parametrem předat dál. Ten inkrement je školometský příklad ale prakticky se to hodí třeba u callbacků. Tam se musí k funkci přibalit nějaká data skoro vždycky.
Sice se to dá ve většině jazyků více i méně rozumně obejít, ale když to podporuje typový systém, tak můžeš podle potřeby  přibalovat data i tam, kde to autora původního kódu ani nenapadlo.

BoneFlute

  • *****
  • 1 987
    • Zobrazit profil
Re:Curryfikované funkce v Haskellu
« Odpověď #3 kdy: 01. 05. 2018, 12:18:16 »
Nebo to má nějaký zajímavý efekt, který mi uniká?
Důležitý rozdíl je třeba ten, že nemusíš předat všechny parametry funkci najednou. Můžeš třeba navázáním jedničky udělat ze součtu inkrement a ten pak jako funkci s jedním parametrem předat dál. Ten inkrement je školometský příklad ale prakticky se to hodí třeba u callbacků. Tam se musí k funkci přibalit nějaká data skoro vždycky.
Sice se to dá ve většině jazyků více i méně rozumně obejít, ale když to podporuje typový systém, tak můžeš podle potřeby  přibalovat data i tam, kde to autora původního kódu ani nenapadlo.
Prosím, psal jsem "Jako já s tím nemám problém. Curryfikování je fajn věc, a umím to použít.".

Takže je tam nějaký zajímavý efekt, který mi uniká?

BoneFlute

  • *****
  • 1 987
    • Zobrazit profil
Re:curryfikované funkce
« Odpověď #4 kdy: 01. 05. 2018, 12:30:18 »
má součinové, součtové a exponenciální typy
Uff! A jdu si studovat :-)


andy

Re:Curryfikované funkce v Haskellu
« Odpověď #5 kdy: 01. 05. 2018, 12:55:47 »
Nebo to má nějaký zajímavý efekt, který mi uniká?
Ano, ve spojení s Haskellovým typovým systémem. Umožňuje to např. psát funkce typu "printf" (tzn. variabilní počet parametrů).

Nebo třeba tohle:

Kód: [Vybrat]
instance Monoid r => Monoid (a -> r) where
   mempty = mempty
   mappend a b = \x -> mappend (a x) (b x)
(snad jsem v tom neudělal chybu) - říká to, že když más funkci, která vrací něco v typeclass Monoid, tak i ta funkce s jedním parametrem je Monoid. No a protože funkce s jedním parametrem je pak monoid, a funkce se dvěma parametry je funkce, která "bere jeden parametr a vrací funkci s jedním parametrem", tak se ta typeclass zase použije. Takže ve výsledku funkce s libovolným počtem parametrů, která vrací Monoid, je Monoid.

Ono se to pak dá použít třeba na to, že převedeš jednu funkci na něco jiného typu... mám třeba funkci typu "Double -> Double -> String" a chci to zabudovat do nějakého interpretu jazyka a na to potřebuju naopak funkci "[Value] -> MyMonad Value". No a přes ty typeclassy se dá dělat "typový" pattern matching na "Double -> r" a tu konverzi provést.

Jinak samozřejmě vzhledem k docela masivnímu používání funkcí vyšších řádů (snad zas nepletu terminologii) je to samozřejmě obrovský syntaktická pomoc, protože pak člověk může psát třeba tyhle věci a nemusí to obalovat lambdama:
Kód: [Vybrat]
filter (> 5) [1..10]
vs.
filter (\x -> x > 5) [1..10]

sortBy (compare `on` age) people
vs.
sortBy (\a b -> compare (age a) (age b)) people

BoneFlute

  • *****
  • 1 987
    • Zobrazit profil
Re:Curryfikované funkce v Haskellu
« Odpověď #6 kdy: 01. 05. 2018, 13:21:25 »
Takže ve výsledku funkce s libovolným počtem parametrů, která vrací Monoid, je Monoid.
To se mi nezdá. Funkce s libovolným počtem parametrů bude Monoid jen po ruční curryfikaci. Automaticky ne.

v

Re:Curryfikované funkce v Haskellu
« Odpověď #7 kdy: 01. 05. 2018, 14:28:21 »
Takže ve výsledku funkce s libovolným počtem parametrů, která vrací Monoid, je Monoid.
To se mi nezdá. Funkce s libovolným počtem parametrů bude Monoid jen po ruční curryfikaci. Automaticky ne.
funkce s libovolným počtem argumentů je funkce s jedním argumentem kde výsledek je funkce s libovolným počtem argumentů minus jedna :)
a0 -> a1 -> ... an -> t
je jako
a0 -> t' kde t' = (a1 -> ... -> an -> t)
nebo taky
a0 -> (a1 -> ... an -> t)

BoneFlute

  • *****
  • 1 987
    • Zobrazit profil
Re:Curryfikované funkce v Haskellu
« Odpověď #8 kdy: 01. 05. 2018, 14:39:53 »
Takže ve výsledku funkce s libovolným počtem parametrů, která vrací Monoid, je Monoid.
To se mi nezdá. Funkce s libovolným počtem parametrů bude Monoid jen po ruční curryfikaci. Automaticky ne.
funkce s libovolným počtem argumentů je funkce s jedním argumentem kde výsledek je funkce s libovolným počtem argumentů minus jedna :)
a0 -> a1 -> ... an -> t
je jako
a0 -> t' kde t' = (a1 -> ... -> an -> t)
nebo taky
a0 -> (a1 -> ... an -> t)
Ha! Rozumím.

Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:Curryfikované funkce v Haskellu
« Odpověď #9 kdy: 02. 05. 2018, 00:17:42 »
Takže ve výsledku funkce s libovolným počtem parametrů, která vrací Monoid, je Monoid.
To se mi nezdá. Funkce s libovolným počtem parametrů bude Monoid jen po ruční curryfikaci. Automaticky ne.
funkce s libovolným počtem argumentů je funkce s jedním argumentem kde výsledek je funkce s libovolným počtem argumentů minus jedna :)
a0 -> a1 -> ... an -> t
je jako
a0 -> t' kde t' = (a1 -> ... -> an -> t)
nebo taky
a0 -> (a1 -> ... an -> t)
Ha! Rozumím.
Ještě doplním, že v signaturách typů je typový operátor → zprava asociativní, takže a→b→c znamená, chceme-li být zcela přesní, a→(b→c) a ta curryfikace v podstatě formálně znamená, že ke každé funkci typu a→b→c existuje isomorfní funkce typu a×b→c (ty funkce nejsou formálně stejné, i když v konkrétní implementaci můžou být identické).

andy

Re:Curryfikované funkce v Haskellu
« Odpověď #10 kdy: 02. 05. 2018, 00:31:06 »
Ještě jeden hezký příklad - tohle je takový "víceparametrový" fmap. Není potřeba používat funkce typu "liftA2", "liftA3" podle počtu parametrů, ale dá se to napsat takhle:
Kód: [Vybrat]
{-# LANGUAGE TupleSections #-}

(,) <$> (neco1 :: Maybe x) <*> (neco2 :: Maybe y)
(,,) <$> (neco1 :: Maybe x) <*> (neco2 :: Maybe y) <*> (neco3 :: Maybe z)
-- Zavorkovani
(((,,) <$> neco1) <*> neco2) <*> neco3
No a když se podíváš na implementaci "fmap"(<$>) a "ap" (<*>), tak vlastně každý z těch kroků zavolá tu funkci s jedním parametrem a vrátí se mu funkce, vyžadující o parametr míň.