Elm Language
Funzioni e applicazione parziale
Ricerca…
Sintassi
- - Definire una funzione senza argomenti è la stessa cosa della semplice definizione di un valore
language = "Elm" - - chiamare una funzione senza argomenti affermando il suo nome
linguaggio - - i parametri sono separati da spazi e seguono il nome della funzione
aggiungi xy = x + y - - chiama una funzione allo stesso modo
aggiungi 5 2 - - applica parzialmente una funzione fornendo solo alcuni dei suoi parametri
incremento = aggiungi 1 - - utilizzare l'operatore |> per passare l'espressione a sinistra alla funzione a destra
dieci = 9 |> incremento - - il <| l'operatore passa l'espressione a destra alla funzione a sinistra
incremento <| aggiungi 5 4 - - catena / comporre due funzioni insieme all'operatore >>
backwardsYell = String.reverse >> String.toUpper - - il << funziona allo stesso modo nella direzione opposta
backwardsYell = String.toUpper << String.reverse - - una funzione con un nome non alfanumerico tra parentesi crea un nuovo operatore
(#) xy = x * y
dieci = 5 # 2 - - qualsiasi operatore infisso diventa una funzione normale quando lo si avvolge tra parentesi
dieci = (+) 5 5 - - le annotazioni del tipo facoltativo compaiono sopra le dichiarazioni di funzione
isTen: Int -> Bool
isTen n = if n == 10 then True else False
Panoramica
La sintassi dell'applicazione delle funzioni in Elm non utilizza parentesi o virgole ed è invece di tipo spazio-sensibile.
Per definire una funzione, specificare il suo nome multiplyByTwo e gli argomenti x , qualsiasi operazione dopo il segno di uguale = è ciò che viene restituito da una funzione.
multiplyByTwo x =
x * 2
Per chiamare una funzione, specificarne il nome e gli argomenti:
multiplyByTwo 2 -- 4
Nota che la sintassi come multiplyByTwo(2) non è necessaria (anche se il compilatore non si lamenta). Le parentesi servono solo a risolvere la precedenza:
> multiplyByTwo multiplyByTwo 2
-- error, thinks it's getting two arguments, but it only needs one
> multiplyByTwo (multiplyByTwo 2)
4 : number
> multiplyByTwo 2 + 2
6 : number
-- same as (multiplyByTwo 2) + 2
> multiplyByTwo (2 + 2)
8 : number
Espressioni Lambda
Elm ha una sintassi speciale per espressioni lambda o funzioni anonime:
\arguments -> returnedValue
Ad esempio, come visto in List.filter :
> List.filter (\num -> num > 1) [1,2,3]
[2,3] : List number
Più alla profondità, una barra rovesciata, \ , è usata per marcare l'inizio dell'espressione lambda, e la freccia, -> , è usata per delimitare gli argomenti dal corpo della funzione. Se ci sono più argomenti, vengono separati da uno spazio:
normalFunction x y = x + y
-- is equivalent to
lambdaFunction = \x y -> x + y
> normalFunction 1 2
3 : number
> lambdaFunction 1 2
3 : number
Variabili locali
È possibile definire variabili locali all'interno di una funzione per
- ridurre la ripetizione del codice
- dare un nome alle sottoespressioni
- ridurre la quantità di argomenti passati.
Il costrutto per questo è let ... in ...
bigNumbers =
let
allNumbers =
[1..100]
isBig number =
number > 95
in
List.filter isBig allNumbers
> bigNumbers
[96,97,98,99,100] : List number
> allNumbers
-- error, doesn't know what allNumbers is!
L'ordine delle definizioni nella prima parte di let non ha importanza!
outOfOrder =
let
x =
y + 1 -- the compiler can handle this
y =
100
in
x + y
> outOfOrder
201 : number
Applicazione parziale
Applicazione parziale significa chiamare una funzione con meno argomenti di quella che ha e salvare il risultato come un'altra funzione (che attende il resto degli argomenti).
multiplyBy: Int -> Int -> Int
multiplyBy x y =
x * y
multiplyByTwo : Int -> Int -- one Int has disappeared! we now know what x is.
multiplyByTwo =
multiplyBy 2
> multiplyByTwo 2
4 : Int
> multiplyByTwo 4
8 : Int
Come sidenote accademico, Elm può farlo a causa del curriculum dietro le quinte.
Valutazione rigorosa e ritardata
In elm, il valore di una funzione viene calcolato quando viene applicato l'ultimo argomento. Nell'esempio seguente, la diagnostica dal log verrà stampata quando f viene richiamato con 3 argomenti o una forma al curry di f viene applicata con l'ultimo argomento.
import String
import Debug exposing (log)
f a b c = String.join "," (log "Diagnostic" [a,b,c]) -- <function> : String -> String -> String -> String
f2 = f "a1" "b2" -- <function> : String -> String
f "A" "B" "C"
-- Diagnostic: ["A","B","C"]
"A,B,C" : String
f2 "c3"
-- Diagnostic: ["a1","b2","c3"]
"a1,b2,c3" : String
A volte vorrai evitare che una funzione venga applicata immediatamente. Un uso tipico in elm è Lazy.lazy che fornisce un'astrazione per il controllo quando vengono applicate le funzioni.
lazy : (() -> a) -> Lazy a
I calcoli pigri prendono una funzione di uno () o argomento di tipo Unit . Il tipo di unità è convenzionalmente il tipo di argomento segnaposto. In un elenco di argomenti, l'argomento corrispondente viene specificato come _ , a indicare che il valore non è utilizzato. Il valore unitario in elm è specificato dal simbolo speciale () che può concettualmente rappresentare una tupla vuota o un foro. Assomiglia alla lista di argomenti vuota in C, Javascript e altri linguaggi che usano le parentesi per le chiamate di funzione, ma è un valore normale.
Nel nostro esempio, f può essere protetto dalla valutazione immediata con un lambda:
doit f = f () -- <function> : (() -> a) -> a
whatToDo = \_ -> f "a" "b" "c" -- <function> : a -> String
-- f is not evaluated yet
doit whatToDo
-- Diagnostic: ["a","b","c"]
"a,b,c" : String
La valutazione delle funzioni viene ritardata ogni volta che una funzione viene parzialmente applicata.
defer a f = \_ -> f a -- <function> : a -> (a -> b) -> c -> b
delayF = f "a" "b" |> defer "c" -- <function> : a -> String
doit delayF
-- Diagnostic: ["a","b","c"]
"a,b,c" : String
Elm ha una funzione always , che non può essere utilizzata per ritardare la valutazione. Poiché Elm valuta tutti gli argomenti delle funzioni indipendentemente e quando il risultato dell'applicazione funzione viene utilizzata, avvolgendo un'applicazione funzione always non causerà un ritardo, perché f è completamente applicata come parametro per always .
alwaysF = always (f "a" "b" "c") -- <function> : a -> String
-- Diagnostic: ["a","b","c"] -- Evaluation wasn't delayed.
Operatori Infix e notazione infisso
Elm consente la definizione di operatori infissi personalizzati.
Gli operatori Infix sono definiti usando le parentesi attorno al nome di una funzione.
Considera questo esempio di operatore infisso per Tuple di costruzione 1 => True -- (1, True) :
(=>) : a -> b -> ( a, b )
(=>) a b =
( a, b )
La maggior parte delle funzioni in Elm sono definite nella notazione prefisso.
Applicare qualsiasi funzione utilizzando la notazione infix specificando il primo argomento prima del nome della funzione racchiuso con carattere grave accento:
import List exposing (append)
append [1,1,2] [3,5,8] -- [1,1,2,3,5,8]
[1,1,2] `append` [3,5,8] -- [1,1,2,3,5,8]