2016-07-02 29 views
8

Con Scalaz, una funzione può essere mappata su un'altra funzione. Quando vorrei utilizzare map su andThen? C'è un chiaro vantaggio usando map? GrazieQuando la mappa su una funzione è utile quando si dispone di e quindi

Per esempio,

val f: Int => Int = (a) => a + 10 

val g: Int => Int = (a) => a * 100 

(f map g map {_*3})(10) == (f andThen g andThen {_*3})(10) // true 
+0

Ben 'map' funziona su qualsiasi funtore, non solo su funzioni. – Bergi

+0

Ci stavo pensando anch'io. Quindi posso mappare un elenco di opzioni e un elenco di functor ma non riesco a capire come creare un elenco di funtori. – thlim

risposta

12

Mettendo da parte i dettagli di implementazione per un momento, mapèandThen per le funzioni (sotto l'istanza functor per A => ?), e in realtà non fanno un sacco di senso parlare di preferire l'uno rispetto all'altro se parliamo di funzioni specifiche e non di un livello più alto di astrazione.

Quali metodi come(e tipo classi come Functor in genere) ci consentono di fare astratti su tipi specifici o costruttori di tipi. Supponiamo di voler scrivere un metodo incrementResult che funzioni sia su A => Int sia su Kleisli[Option, A, Int], ad esempio. Questi tipi non hanno nulla in comune in termini di eredità (a corto di AnyRef, che è inutile), ma A => ? e Kleisli[Option, A, ?] sono entrambi funtori, così abbiamo potuto scrivere questo:

import scalaz._, Scalaz._ 

def incrementResult[F[_]: Functor](f: F[Int]): F[Int] = f.map(_ + 1) 

e quindi utilizzarlo come questo (notare che io sto usando kind-projector per semplificare la sintassi di tipo un po '):

scala> val plainOldFuncTriple: Int => Int = _ * 3 
plainOldFuncTriple: Int => Int = <function1> 

scala> val optionKleisliTriple: Kleisli[Option, Int, Int] = Kleisli(i => Some(i * 3)) 
optionKleisliTriple: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>) 

scala> val f = incrementResult[Int => ?](plainOldFuncTriple) 
f: Int => Int = <function1> 

scala> val k = incrementResult[Kleisli[Option, Int, ?]](optionKleisliTriple) 
k: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>) 

scala> f(10) 
res0: Int = 31 

scala> k(10) 
res1: Option[Int] = Some(31) 

In questo caso specifico ci sono modi migliori per attuare questa operazione, ma si vede il generale idea-non siamo riusciti a scrivere un metodo singolo che funziona sia per le funzioni ordinarie che per le frecce di Kleisli utilizzando andThen, ma possiamo con il livello extra di astrazione che ci offre map.

Quindi per rispondere alla tua domanda uso-un'affermazione da map se si desidera astratto su tutti i costruttori di tipo che hanno un esempio funtore, ma se si sta lavorando in particolare con le funzioni, mapèandThen, e-il più a lungo dato che stiamo ancora mettendo da parte i dettagli di implementazione, non importa quale scegli.


Nota: il map che il pacchetto di Scalaz syntax si dà per valori di tipi che hanno casi funtore è implementato come un metodo di estensione, quindi c'è un po 'di testa (sia in fase di compilazione e di runtime) che comporta l'utilizzazione map anziché andThen su una funzione. Se lavori solo con le funzioni e non hai bisogno di astrazione extra, allora potresti anche andare con andThen.

+1

Possiamo * scrivere * un singolo metodo che funziona sia con 'che Then':' def incrementaResult [~> [_, _]: Componi, A] (f: A ~> Int): A ~> Int = f e poi (_ + 1) ' –

+1

@ JulienRichard-Foy Sure (è quello che intendevo per" modi migliori per implementare questa operazione), ma è una specie di distrazione dato che 'andThen' non è il' and Then' su 'Function1', che è quello che chiede l'OP –

+0

@ JulienRichard-Foy come aggiustare questo valore? 'e quindi non è un membro del parametro type ~> [A, Int]' – thlim