Frage

Was sind die weniger bekannten, aber nützliche Features der Programmiersprache Haskell. (Ich verstehe die Sprache selbst ist weniger bekannt, aber die Arbeit mit mir. Auch Erklärungen zu den einfachen Dingen in Haskell, wie die Definition der Fibonacci-Folge mit einer Zeile Code, wird von mir bekommen upvoted.)

  • Versuchen Sie Antworten auf die Haskell Kern zu begrenzen
  • Ein Merkmal pro Antwort
  • Geben Sie ein Beispiel und eine kurze Beschreibung der Funktion, nicht nur einen Link zur Dokumentation
  • Beschriften Sie die Funktion mit kühnen Titel als erste Zeile
War es hilfreich?

Lösung

Mein Gehirn gerade explodiert

Wenn Sie versuchen, diesen Code zu kompilieren:

{-# LANGUAGE ExistentialQuantification #-}
data Foo = forall a. Foo a
ignorefoo f = 1 where Foo a = f

Sie erhalten diese Fehlermeldung:

$ ghc Foo.hs

Foo.hs:3:22:
    My brain just exploded.
    I can't handle pattern bindings for existentially-quantified constructors.
    Instead, use a case-expression, or do-notation, to unpack the constructor.
    In the binding group for
        Foo a
    In a pattern binding: Foo a = f
    In the definition of `ignorefoo':
        ignorefoo f = 1
                    where
                        Foo a = f

Andere Tipps

Benutzerdefinierte Kontrollstrukturen

Haskell hat keine Stenografie ternären Operator. Der eingebaute in if-then-else ist immer ternärer und ist ein Ausdruck (imperative Sprachen sind in der Regel ?: = Ausdruck, if = Anweisung haben). Wenn Sie wollen, obwohl,

True ? x = const x
False ? _ = id

wird (?) definiert der ternäre Operator sein:

(a ? b $ c)  ==  (if a then b else c)

Sie müssten Makros in den meisten anderen Sprachen greifen Sie Ihre eigenen Kurzschließen logischen Operatoren zu definieren, aber Haskell ist eine voll faul Sprache, so dass es funktioniert einfach.

-- prints "I'm alive! :)"
main = True ? putStrLn "I'm alive! :)" $ error "I'm dead :("

Hoogle

Hoogle ist dein Freund. Ich gebe zu, es ist nicht Teil des „Kerns“, so cabal install hoogle

Jetzt wissen Sie, wie „wenn Sie sich für eine Funktion höherer Ordnung suchen, es ist schon da“ ( ephemient Kommentar ). Aber wie finden Sie diese Funktion? Mit Hoogle!

$ hoogle "Num a => [a] -> a"
Prelude product :: Num a => [a] -> a
Prelude sum :: Num a => [a] -> a

$ hoogle "[Maybe a] -> [a]"
Data.Maybe catMaybes :: [Maybe a] -> [a]

$ hoogle "Monad m => [m a] -> m [a]"
Prelude sequence :: Monad m => [m a] -> m [a]

$ hoogle "[a] -> [b] -> (a -> b -> c) -> [c]"
Prelude zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

Die Hoogle-Google-Programmierer sind nicht in der Lage seine Programme auf Papier zu schreiben, indem er sich die gleiche Art, wie er mit Hilfe des Computers. Aber er und die Maschine zusammen * ein erzwungener nicht gerechnet werden.

Btw, wenn Sie gerne Hoogle sicher sein hlint zu überprüfen!

Free Theoreme

Phil Wadler stellte uns die Vorstellung von einem freien Satz rel="nofollow und wir haben sie in Haskell seitdem zu missbrauchen.

Diese wunderbaren Artefakte von Hindley-Milner-Style-Systemen helfen, indem Sie Parametrizität mit equational Argumentation aus Ihnen sagen, was eine Funktion wird nicht tun.

Zum Beispiel gibt es zwei Gesetze, die jede Instanz Functor erfüllen sollte:

  1. forall f g. fmap f. fmap g = fmap (f. g)
  2. fmap id = id

Aber der freie Satz sagt uns, wir kümmern müssen nicht die erste zu beweisen, aber angesichts der zweiten kommt es für ‚frei‘ nur von der Art Unterschrift!

fmap :: Functor f => (a -> b) -> f a -> f b

Sie müssen ein bisschen vorsichtig mit Faulheit sein, aber dies teilweise in dem ursprünglichen Papier bedeckt ist, und in Janis Voigtlaender des neueres Papier auf freie Theoreme in Gegenwart von seq.

Stenografie für eine gemeinsame Liste Operation

Die folgenden sind äquivalent:

concat $ map f list
concatMap f list
list >>= f

Bearbeiten

Seit mehr Details wurden aufgefordert, ...

concat :: [[a]] -> [a]

concat nimmt eine Liste von Listen und verkettet sie in einer einzigen Liste.

map :: (a -> b) -> [a] -> [b]

map bildet eine Funktion über eine Liste.

concatMap :: (a -> [b]) -> [a] -> [b]

concatMap ist (.) concat . map äquivalent: eine Funktion, über eine Liste Karte und die Ergebnisse verketten

.
class Monad m where
    (>>=) :: m a -> (a -> m b) -> m b
    return :: a -> m a

A Monad hat eine bind Operation, die in >>= Haskell (oder dessen gezuckerten do-Äquivalent) aufgerufen wird. Liste, auch bekannt als [], ist ein Monad. Wenn wir [] für m in der oben ersetzen:

instance Monad [] where
    (>>=) :: [a] -> (a -> [b]) -> [b]
    return :: a -> [a]

Was ist die natürlichste Sache für die Monad Operationen auf einer Liste zu tun? Wir müssen die Monade Gesetze erfüllen,

return a >>= f           ==  f a
ma >>= (\a -> return a)  ==  ma
(ma >>= f) >>= g         ==  ma >>= (\a -> f a >>= g)

Sie können überprüfen, ob diese Gesetze halten, wenn wir die Umsetzung

verwenden
instance Monad [] where
    (>>=) = concatMap
    return = (:[])

return a >>= f  ==  [a] >>= f  ==  concatMap f [a]  ==  f a
ma >>= (\a -> return a)  ==  concatMap (\a -> [a]) ma  ==  ma
(ma >>= f) >>= g  ==  concatMap g (concatMap f ma)  ==  concatMap (concatMap g . f) ma  ==  ma >>= (\a -> f a >>= g)

Das ist in der Tat, das Verhalten von Monad []. Als Demonstration,

double x = [x,x]
main = do
    print $ map double [1,2,3]
        -- [[1,1],[2,2],[3,3]]
    print . concat $ map double [1,2,3]
        -- [1,1,2,2,3,3]
    print $ concatMap double [1,2,3]
        -- [1,1,2,2,3,3]
    print $ [1,2,3] >>= double
        -- [1,1,2,2,3,3]

Nestbar mehrzeilige Kommentare .

{- inside a comment,
     {- inside another comment, -}
still commented! -}

algebraische Datentypen verallgemeinert. Hier ist ein Beispiel-Interpreter, wo der Typ-System können Sie alle Fälle abdecken:

{-# LANGUAGE GADTs #-}
module Exp
where

data Exp a where
  Num  :: (Num a) => a -> Exp a
  Bool :: Bool -> Exp Bool
  Plus :: (Num a) => Exp a -> Exp a -> Exp a
  If   :: Exp Bool -> Exp a -> Exp a -> Exp a 
  Lt   :: (Num a, Ord a) => Exp a -> Exp a -> Exp Bool
  Lam  :: (a -> Exp b) -> Exp (a -> b)   -- higher order abstract syntax
  App  :: Exp (a -> b) -> Exp a -> Exp b
 -- deriving (Show) -- failse

eval :: Exp a -> a
eval (Num n)      = n
eval (Bool b)     = b
eval (Plus e1 e2) = eval e1 + eval e2
eval (If p t f)   = eval $ if eval p then t else f
eval (Lt e1 e2)   = eval e1 < eval e2
eval (Lam body)   = \x -> eval $ body x
eval (App f a)    = eval f $ eval a

instance Eq a => Eq (Exp a) where
  e1 == e2 = eval e1 == eval e2

instance Show (Exp a) where
  show e = "<exp>" -- very weak show instance

instance (Num a) => Num (Exp a) where
  fromInteger = Num
  (+) = Plus

Muster in Top-Level-Bindungen

five :: Int
Just five = Just 5

a, b, c :: Char
[a,b,c] = "abc"

Wie cool ist das! Speichert Sie diesen Anruf fromJust und head ab und zu.

Optional Layout-

Sie können explizite Klammern und Semikolons anstelle von Leerzeichen (aka Layout) Blöcke zu begrenzen.

let {
      x = 40;
      y = 2
     } in
 x + y

... oder äquivalent ...

let { x = 40; y = 2 } in x + y

... statt ...

let x = 40
    y = 2
 in x + y

Da das Layout nicht erforderlich ist, können Haskell Programme ohne weiteres von anderen Programmen erzeugt werden.

seq und ($!) nur genug bewerten überprüfen dass etwas nicht unten.

Das folgende Programm wird nur drucken "dort".

main = print "hi " `seq` print "there"

Für jene nicht vertraut mit Haskell, Haskell ist nicht streng im Allgemeinen, was bedeutet, dass ein Argument eine Funktion wird nur ausgewertet, wenn es gebraucht wird.

Zum Beispiel können die folgenden Drucke „ignoriert“ und enden mit Erfolg.

main = foo (error "explode!")
  where foo _ = print "ignored"

seq ist bekannt, dass das Verhalten zu ändern, indem Sie nach unten zu überprüfen sein erstes Argument ist unten.

Zum Beispiel:

main = error "first" `seq` print "impossible to print"

... oder äquivalent, ohne Infix ...

main = seq (error "first") (print "impossible to print")

... mit einem Fehler auf „ersten“ sprengen. Es wird nie „unmöglich drucken“.

drucken

So könnte es ein wenig überraschend sein, dass, obwohl seq streng ist, wird es nicht etwas, die Art und Weise eifrig Sprachen auswerten bewerten. Insbesondere werden versuchen, es nicht alle positiven ganzen Zahlen in dem folgenden Programm zu erzwingen. Stattdessen wird es prüfen, ob [1..] nicht unten (die sofort gefunden werden kann), print „done“, und beenden.

main = [1..] `seq` print "done"

Operator Unveränderlichkeit

Sie können das Infix, infixl oder infixr Schlüsselwörter Operatoren definieren Assoziativität und Vorrang. Beispiel aus dem Referenz :

main = print (1 +++ 2 *** 3)

infixr  6 +++
infixr  7 ***,///

(+++) :: Int -> Int -> Int
a +++ b = a + 2*b

(***) :: Int -> Int -> Int
a *** b = a - 4*b

(///) :: Int -> Int -> Int
a /// b = 2*a - 3*b
Output: -19

Die Zahl (0 bis 9) nach dem Infix ermöglicht es Ihnen, den Vorrang des Operators zu definieren, wobei 9 die stärksten. Infix bedeutet keine Assoziativität, während infixl Mitarbeiter verlassen und infixr Mitarbeiter rechts.

Auf diese Weise können Sie komplexe Operatoren definieren hohe Operationen als einfache Ausdrücke geschrieben zu tun.

Beachten Sie, dass Sie auch binäre Funktionen als Operatoren verwenden können, wenn man sie zwischen Backticks platzieren:

main = print (a `foo` b)

foo :: Int -> Int -> Int
foo a b = a + b

Und als solche, können Sie auch Vorrang für sie definieren:

infixr 4 `foo`

Vermeiden von Klammern

Die (.) und ($) Funktionen in Prelude haben sehr bequem fixities, so dass Sie Klammern an vielen Stellen vermeiden. Die folgenden sind äquivalent:

f (g (h x))
f $ g $ h x
f . g $ h x
f . g . h $ x

flip hilft auch, die sind äquivalent:

map (\a -> {- some long expression -}) list
flip map list $ \a ->
    {- some long expression -}

Pretty Wächter

Prelude definiert otherwise = True, so dass komplette Abdeckung Bedingungen sehr natürlich lesen.

fac n
  | n < 1     = 1
  | otherwise = n * fac (n-1)

C-Style Aufzählungen

Die Kombination von Top-Level-Pattern-Matching und arithmetische Folgen geben uns eine praktische Möglichkeit, aufeinanderfolgende Werte zu definieren:

foo : bar : baz : _ = [100 ..]    -- foo = 100, bar = 101, baz = 102

Lesbare Funktion Zusammensetzung

Prelude definiert (.) mathematische Funktion Zusammensetzung zu sein; das heißt, g . f ersten f gilt, dann gilt g auf das Ergebnis.

Wenn Sie import Control.Arrow, das sind äquivalent:

g . f
f >>> g

Control.Arrow bietet eine instance Arrow (->), und das ist gut für Leute, die nicht gerne rückwärts Funktion Anwendung lesen.

let 5 = 6 in ... gilt Haskell.

Infinite Listen

Da Sie Fibonacci erwähnt, gibt es eine sehr elegante Art und Weise von Erzeugungs Fibonacci-Zahlen aus einer unendlichen Liste wie folgt aus:

fib@(1:tfib)    = 1 : 1 : [ a+b | (a,b) <- zip fib tfib ]

Der Operator @ können Sie Muster auf dem 1 passend verwenden: tfib Struktur, während immer noch auf das gesamte Muster als fib Bezug genommen wird.

Beachten Sie, dass das Verständnis Liste tritt in eine Endlosschleife, eine unendliche Liste zu erzeugen. Sie können jedoch Elemente von ihm verlangen, oder betreiben sie, solange Sie eine endliche Menge anfordern:

take 10 fib

Sie können auch eine Operation auf alle Elemente anwenden, bevor sie Ihr Interesse an:

take 10 (map (\x -> x+1) fib)

Dies ist dank Haskells faul Auswertung von Parametern und Listen.

Flexible Spezifikation des Moduls Importe und Exporte

Importieren und Exportieren von schön ist.

module Foo (module Bar, blah)  -- this is module Foo, export everything that Bar expored, plus blah

import qualified Some.Long.Name as Short
import Some.Long.Name (name)  -- can import multiple times, with different options

import Baz hiding (blah)  -- import everything from Baz, except something named 'blah'

Wenn Sie sich für eine Liste oder Funktion höherer Ordnung, es ist schon da

suchen

Es gibt sooo viele Komfort und Funktionen höherer Ordnung in der Standardbibliothek.

-- factorial can be written, using the strict HOF foldl':
fac n = Data.List.foldl' (*) 1 [1..n]
-- there's a shortcut for that:
fac n = product [1..n]
-- and it can even be written pointfree:
fac = product . enumFromTo 1

Equational Reasoning

Haskell, wobei rein funktional ermöglicht es Ihnen, ein Gleichheitszeichen als reale Gleichheitszeichen (in Abwesenheit von nicht-überlappenden Mustern).

lesen

Auf diese Weise können Sie Definitionen direkt in den Code ersetzen, und in Bezug auf die Optimierung gibt viel Spielraum für den Compiler darüber, wann Sachen passiert.

Ein gutes Beispiel für diese Form der Argumentation kann hier gefunden werden:

http://www.haskell.org/pipermail /haskell-cafe/2009-March/058603.html

Dies äußert sich auch gut in Form von Gesetzen oder Regeln Pragmas für gültige Mitglieder einer Instanz zu erwarten, zum Beispiel die Monade Gesetze:

  1. returrn a >> = f == f a
  2. m >> = Rückkehr == m
  3. (m >> = f) >> = g == m >> = (\ x -> f x >> = g)

kann oft verwendet werden monadischen Code zu vereinfachen.

Laziness

Ubiquitous Faulheit bedeutet, dass Sie Dinge tun können wie definieren

fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

Aber es gibt uns auch viel subtile Vorteile in Bezug auf Syntax und Argumentation.

Zum Beispiel aufgrund Strikt ML mit dem Werteinschränkung , und ist sehr vorsichtig Kreis let Bindungen zu verfolgen, aber in Haskell, können wir jede rekursiv sein lassen lassen und haben keine Notwendigkeit, zwischen val und fun zu unterscheiden. Dies beseitigt eine große syntaktische Warze aus der Sprache.

Das gibt indirekt Anlass zu unserer schönen where Klausel, weil wir sicher Berechnungen bewegen können, die nicht aus dem Hauptkontrollfluss verwendet werden können, oder sie können und Faulheit Deal lassen mit den Ergebnissen zu teilen.

Wir können (fast) alle diese ML Stil Funktionen ersetzen, die () und geben einen Wert zurück, mit nur einer faulen Berechnung des Wertes nehmen müssen. Es gibt Gründe, dabei von Zeit zu Zeit zu vermeiden, undichten Raum zu vermeiden, mit CAFs , aber solche Fälle sind selten.

Schließlich erlaubt es uneingeschränkte eta-Reduktion (\x -> f x kann mit f ersetzt werden). Dies macht combinator orientierte Programmierung für Dinge wie Parser Kombinatoren viel angenehmer als mit ähnlichen Konstrukten in einer strengen Sprache arbeiten.

Dies hilft Ihnen, wenn es um Programme in Punkt-freien Art Argumentation, oder über sie in Punkt-freien Art Umschreiben und reduziert Argument Rauschen.

Parallel Liste Verständnis

(Special GHC-Funktion)

  fibs = 0 : 1 : [ a + b | a <- fibs | b <- tail fibs ]

Verbesserte Mustervergleich

  • Faule Muster
  • Irrefutable Muster

    let ~(Just x) = someExpression
    

Siehe Mustervergleich

Aufzählungen

Jede Art, die eine Instanz von ist Enum kann in einer arithmetischen Folge verwendet wird, nicht nur Zahlen:

alphabet :: String
alphabet = ['A' .. 'Z']

Ihre eigenen Datentypen einschließlich, nur von Enum ableiten eine Standardimplementierung zu bekommen:

data MyEnum = A | B | C deriving(Eq, Show, Enum)

main = do
    print $ [A ..]                 -- prints "[A,B,C]"
    print $ map fromEnum [A ..]    -- prints "[0,1,2]"

Monaden

Sie sind nicht so versteckt, aber sie sind einfach überall, auch dort, wo man von ihnen nicht denkt (Listen, Vielleicht-Typ) ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top