2010-11-11 5 views
8

Si tengo una instancia de Bifunctor[A,A] bf, una función f : A => A y un valor Booleanp:La invocación de una función en un "lado" de un bifuntor depende del valor de un booleano

def calc[A, F[_,_]: Bifunctor](p: Boolean, bf: F[A, A], f: A => A): F[A, A] = { 
    val BF = implicitly[Bifunctor[F]] 
    BF.bimap(bf, (a : A) => if (p) f(a) else a, (a : A) => if (!p) f(a) else a) 
} 

¿Cómo puedo poner esto de manera más concisa (y expresiva)? Básicamente, estoy tratando de invocar una función en un lado de un bifunctor (por ejemplo, un Tuple2) dependiendo de algún predicado. Si el predicado es cierto, quiero asignar el LHS y el RHS si es falsa

val t2 = (1, 2) 
def add4 = (_ : Int) + 4 
calc(true, t2, add4) //should be (5,2) 
calc(false, t2, add4) //should be (1,6) 


Dado que yo quiero usar tuplas (a diferencia de la más general Bifunctor), que parecen ser capaces utilizar arrows de la siguiente manera:

def calc[A](p: Boolean, bf: (A, A), f: A => A): (A, A) 
    = (if (p) f.first[A] else f.second[A]) apply bf 

Respuesta

4

No todo lo que mucho más agradable:

def calc[A, F[_,_]:Bifunctor](p: Boolean, bf: F[A, A], f: A => A): F[A, A] = 
    (if (p) (bf :-> (_: A => A)) else ((_:A => A) <-: bf))(f) 

Un poco más agradable:

def cond[A:Zero](b: Boolean, a: A) = if (b) a else mzero 

def calc[A, F[_,_]:Bifunctor](p: Boolean, bf: F[A, A], f: Endo[A]): F[A, A] = 
    cond(p, f) <-: bf :-> cond(!p, f) 

Algunos Haskell, sólo por la envidia idioma:

calc p = if p then first else second 
+0

El problema es que estoy escribiendo esto * en línea * (es decir, no quiero declarar un método adicional), así que no quiero tener que escribir 'bf' dos veces, porque es (en hecho) el resultado de una invocación a un método. La solución de flechas que encontré realmente funciona muy bien –

+0

Debo decir que también quiero acceder solo a 'p' una vez también –

+0

Para la solución de Haskell, ¿no necesita aplicar el resultado al bifunctor? ¿O es eso implícito? (No sé, haskell) –

0

Editar: fija para volver (A,A) en lugar de A

Tal vez me falta algo, pero ¿no es esto para lo que son las variables temporales? Con una tupla Scala normal:

Some(bf).map(x => if (p) x.copy(_1 = f(x._1)) else x.copy(_2 = f(x._2))).get 

o

{ val (l,r) = bf; if (p) (f(l),r) else (l,f(r)) } 
+0

¿Puedes hacer eso con bifuncionantes arbitrarios? – Apocalisp

+0

@Apocalisp - Probablemente no (no he tenido la oportunidad de usar bifuncionantes arbitrarios, así que no conozco bien sus propiedades), pero estaba saliendo de la parte "dado que quiero usar tuplas" del pregunta. –

+0

Bastante justo. Los bifunctores solo tienen un método 'def bimap [A, B, C, D] (k: F [A, B], f: A => C, g: B => D): F [C, D]', satisfaciendo algunas leyes obvias (dadas por parametricidad). – Apocalisp

0

¿Esta variante de Apocalisp's solution trabajo?

def calc[A, F[_,_]:Bifunctor](p: Boolean, bf: F[A, A], f: A => A): F[A, A] = 
    (if (p) ((_: F[A,A]) :-> f) else (f <-: (_: F[A,A])))(bf) 

Nota: No he probado esto con scalaz.

Cuestiones relacionadas