2015-12-30 19 views
5

Ich arbeite gerade durch einige einfache Übungen in Haskell und fragte mich, ob es eine point-free Möglichkeit zur Konvertierung einer Wenn-Dann-sonst-Anweisung in einen Maybe Typ: Nothing zurückgegeben wurde, wenn die Bedingung falsch ist, und Just die Eingabe wenn die Bedingung wahr ist.Gibt es eine point-free Möglichkeit, eine bedingte Prüfung in einen Maybe-Typ der Eingabe umzuwandeln?

Kurz gesagt, da einige:

maybeIf :: (a -> Bool) -> a -> Maybe a 
maybeIf cond a = if cond a then Just a else Nothing 

Gibt es eine Implementierung, die in Bezug auf a Punkt frei ist? Ich habe mir auch eine konkretere Version angesehen, a -> Maybe a, und ich habe das Gefühl, dass es irgendwo eine Antwort geben kann in Control.Arrow. Da Maybe jedoch ein Datentyp ist und if-else-Anweisungen den Datenfluss steuern, bin ich nicht sicher, ob es einen sauberen Weg dafür gibt.

Antwort

7

Sie find von Data.Foldable importieren und es ist dann ganz einfach:

import Data.Foldable(find) 

maybeIf cond = find cond . Just 

Die Funktion find ist nicht kompliziert, so dass Sie ganz leicht könnte definieren es selbst weniger allgemein, in Bezug auf Maybe, aber es ist nicht t tatsächlich so anders als Ihre eigene Implementierung von maybeIf, so dass Sie nicht viel gewinnen können, je nachdem, warum Sie es tun wollten.

6

Die Hauptsache, die diesen Punkt frei macht, ist der if/then/else. Sie können einen if' combinator definieren, oder Sie können diese verallgemeinerte Version verwenden, die ich definieren und verwenden oft:

ensure p x = x <$ guard (p x) 

Standard-Tools geben aufeinanderfolgende punktfreie Versionen als

ensure p = ap (<$) (guard . p) 
ensure = ap (<$) . (guard .) 

obwohl ich nicht wirklich tun denke entweder, sind besser als die sinnvolle Version.

+2

'Data.Bool' hat' bool'. – dfeuer

+1

Sehr interessant! Ich denke, dass mir diese Antwort am besten gefällt, da sie das meiste Material auf einfache und prägnante Weise zu lernen hat. Ich werde dies @Peter geben, da seine Antwort am lesbarsten ist und ich denke, dass ich nur um Punkt-frei gebeten habe. – stites

3

Wenn wir eine Kirche-Codierung für Boolesche Werte wählen ...

truth :: Bool -> a -> a -> a 
truth True t f = t 
truth False t f = f 

Dann können wir einen Punkt frei maybeIf in Applicative-Stil schreiben.

maybeIf :: (a -> Bool) -> a -> Maybe a 
maybeIf = liftA3 truth <*> pure Just <*> pure (pure Nothing) 

Einige Intuitionen ...

f <$> m₁ <*> … <*> mₙ = \x -> f (m₁ x) … (mₙ x) 
liftAₙ f <$> m₁ <*> … <*> mₙ = \x -> f <$> m₁ x <*> … <*> mₙ x 

Hier ist ein Rendering im PNG-Format der oben genannten „Intuitionen“, Ihre installierten Schriftarten, falls nicht die benötigten Unicode-Zeichen unterstützen.

enter image description here

So also:

liftA3 truth <*> pure Just <*> pure (pure Nothing) 
= liftA3 truth <$> id <*> pure Just <*> pure (pure Nothing) 
= \p -> truth <$> id p <*> (pure Just) p <*> (pure (pure Nothing)) p 
= \p -> truth <$> p <*> Just <*> pure Nothing 
= \p -> \a -> truth (p a) (Just a) ((pure Nothing) a) 
= \p -> \a -> truth (p a) (Just a) Nothing 
+0

Ich kann einige der Charaktere nicht sehen! Nach "Einige Intuitionen ..." sind die Codeblock-Indizes (?), Die nicht "1" sind, eingerahmte Fragezeichen. – stites

+1

@stites Ich habe ein Rendering hinzugefügt, das Sie sehen können. – erisco

2

dfeuer's lead verfolgt (und Daniel Wagner neuen Namen für diese Funktion),

import Data.Bool (bool) 
--   F T  
-- bool :: a -> a -> Bool -> a 

ensure :: (a -> Bool) -> a -> Maybe a 
ensure p x = bool (const Nothing) Just (p x) x 

ensure p = join (bool (const Nothing) Just . p) 
      = bool (const Nothing) Just =<< p 

ensure  = (bool (const Nothing) Just =<<) 

join ist eine einstellige Funktion, join :: Monad m => m (m a) -> m a, aber für Funktionen ist es einfach

join k x = k x x 
(k =<< f) x = k (f x) x 

join wird akzeptiert ein Ersatz für W combinator in Punkt-freien Code.

Sie wollten es nur in Bezug auf den Wert Argument punktfrei, aber es ist leicht, die Gleichung mit join weiter (Lesbarkeit des Ergebnisses ist ein anderes Thema insgesamt), wie

  = join ((bool (const Nothing) Just .) p) 
      = (join . (bool (const Nothing) Just .)) p 

Tat zu verwandeln

#> (join . (bool (const Nothing) Just .)) even 3 
Nothing 

#> (bool (const Nothing) Just =<<) even 4 
Just 4 
+1

@dfeuer Ich habe bearbeitet, danke. Der resultierende Ausdruck ist unauffällig, aber zumindest jetzt ist es pointfree. –

+1

Hm .... Ich frage mich, ob es irgendeinen Sinn in der Verallgemeinerung zu finden gibt 'sure p = join (bool (const leer) rein. P)'. – dfeuer

+0

@dfeuer nett! es ist viel klarer so ... und mit der neuen Basis ist das dasselbe wie Daniel Wagners ausgezeichnetes '(<$) <*> guard. p', seit jetzt [' guard :: Alternative f => Bool -> f() '] (http : //hackage.haskell.org/package/base-4.8.1.0/docs/Control-Monad.html#v: guard)! (also nicht 'MonadPlus') –