2015-06-10 19 views
5

Considerare le funzioni del tipo a -> b -> c e i valori applicativi a1, a2 :: (Applicative f) => f a.

Desidero costruire una funzione che può essere applicata alle funzioni di tipo a -> b -> c per ottenere i valori di tipo Applicative f :: f c. Posso fare questo nel modo seguente:

g :: (Applicative f) => (a -> b -> c) -> f c 
g = \f -> f <$> a1 <*> a2 

(Il lambda esplicita è deliberata, come sto considerando la costruzione di questa funzione a qualsiasi livello, non solo il livello superiore).

Se provo a scrivere g in stile libero-point:

g = (<$> a1 <*> a2) 

ottengo il seguente errore di compilazione:

The operator `<$>' [infixl 4] of a section 
    must have lower precedence than that of the operand, 
     namely `<*>' [infixl 4] 
    in the section: `<$> gen1 <*> gen2' 

ho potuto scrivere questa implementazione libera-punto:

g = flip (flip liftA2 a1) a2 

ma credo che questo sia le ss leggibile, ed è più semplice per refactoring l'implementazione basata sulla funzione infisso per, ad esempio, aggiungere un altro argomento, che modificare il precedente per utilizzare liftA3.

Si può scrivere una catena di composizioni:

g = (<*> a2) . (<$> a1) 

Questo raggiunge free style punto ed è semplice aggiungere argomenti - ma ottengono anteposto a sinistra invece che aggiunto a destra, così si perde la corrispondenza con la funzione tipo (a -> b -> c). Inoltre, con più argomenti si finisce con un'espressione molto più lunga rispetto all'utilizzo di un lambda come nella prima implementazione.

Quindi, c'è un modo carino e teso per scrivere la sezione che desidero o sono bloccato con un lambda?

+0

a Scala si potrebbe avere '_ <$> un b' <*> ma Haskell non ha tale sintassi. –

+0

@ErikAllik Questa è una sintassi interessante! Mi chiedo se qualcuno abbia scritto un'estensione template-haskell per questo. Sarebbe fantastico. – AJFarmar

+0

ma '_' è già stato preso da buchi digitati; forse qualcosa di simile però; sebbene la sintassi lambda sia abbastanza buona. –

risposta

7

<*> opera sul risultato di <$>, quindi:

g = (<*> a2) . (<$> a1) 
+2

@frasertweedale: In tal caso, utilizzerei solo il tuo primo metodo. Non è come se dovesse essere un lambda, però. 'g f = f a1 <*> a2' – Ryan

+4

@frasertweedale: Anche quando non è solo il livello principale, puoi usare' let' ecc .. Meno verbosità non significa più chiarezza. – Ryan

6

Non sono davvero convinto pointfree può essere fatta meglio che usare un argomento esplicito qui, ma un altro paio di idee:

  • È possibile utilizzare flip infisso:

    g = liftA2 `flip` a1 `flip` a2 
    
  • È possibile utilizzare >>> da Control.Category, che è . capovolto:

    g = (<$> a1) >>> (<*> a2)