2017-10-30 21 views
2

저는 카레 함수를 추상화하는 방법을 알아 내려고하고 있습니다.Scala + Shapeless 추상 이상의 카레 함수

def liftAU[F, P <: Product, L <: HList, R, A[_]](f: F) 
(implicit 
fp: FnToProduct.Aux[F, L => R], 
gen: Generic.Aux[P, L], 
ap: Applicative[A] 
): A[P] => A[R] = p => p.map(gen.to).map(f.toProduct) 

이 (INT, INT) => 지능과 같은 기능을 가지고 옵션과 같이 [(INT, INT)] => 옵션으로 바꿀 것 나는 통해 uncurried 기능을 통해 추상적 인 수했습니다 [Int]. 그리고 그것은 기능의 모든 아리 덕분에 작동합니다.

Int => Int => Int와 같은 함수를 취해 Option [Int] => Option [Int] => Option [Int]로 변환 할 카레 버전을 만들고 싶습니다.

또한 카레 기능의 모든 작동에 적합해야합니다.

FnToProduct는 첫 번째 매개 변수 목록에서만 작동하므로 여기서는 도움이되지 않습니다. 또한 유형 수준에서 재귀 정의를 작성하려고 시도했지만 형식을 정의하는 데 문제가 있습니다.

가능한지 확실하지 않지만 다른 사람들이 이와 같은 시도를했는지 알고 싶습니다.

답변

3

내가 개체 중 하나의 인스턴스 이름을 변경, 심지어 그때는 Int => Int => Int => Int 같은 기능이 작동하지 않습니다하지 않는 한 드미트로의 대답은 실제로 나를 위해 작동하지 않습니다 , 그리고 나는 Poly 값으로 작업하는 것이 정말 짜증이났다는 것을 알았습니다. 이전 답변을 디버깅하는 대신에, 저는 직접 작성하려고합니다.

import cats.Applicative 

trait LiftCurried[F[_], I, O] { 
    type Out 
    def apply(f: F[I => O]): F[I] => Out 
} 

object LiftCurried extends LowPriorityLiftCurried { 
    implicit def liftCurried1[F[_]: Applicative, I, I2, O2](implicit 
    lc: LiftCurried[F, I2, O2] 
): Aux[F, I, I2 => O2, F[I2] => lc.Out] = new LiftCurried[F, I, I2 => O2] { 
    type Out = F[I2] => lc.Out 
    def apply(f: F[I => I2 => O2]): F[I] => F[I2] => lc.Out = 
     (Applicative[F].ap(f) _).andThen(lc(_)) 
    } 
} 

trait LowPriorityLiftCurried { 
    type Aux[F[_], I, O, Out0] = LiftCurried[F, I, O] { type Out = Out0 } 

    implicit def liftCurried0[F[_]: Applicative, I, O]: Aux[F, I, O, F[O]] = 
    new LiftCurried[F, I, O] { 
     type Out = F[O] 
     def apply(f: F[I => O]): F[I] => F[O] = Applicative[F].ap(f) _ 
    } 
} 

조금 청소기 있는지 확인하는 아마 가능하지만, 나는 그것이 합리적인 읽을 그대로 찾을 :

하기는 꽤 잘 100 % 볼품없는 타입 클래스를 사용하여이 작업을 쓸 수 있습니다.

이 같은 콘크리트가 할 수 있습니다 :

def liftCurriedIntoOption[I, O](f: I => O)(implicit 
    lc: LiftCurried[Option, I, O] 
): Option[I] => lc.Out = lc(Some(f)) 

을 그리고 우리는이 같은 일부 기능 작동하는지 보여줄 수 : 다음

val f: Int => Int => Int = x => y => x + y 
val g: Int => Int => Int => Int = x => y => z => x + y * z 
val h: Int => Int => Int => String => String = x => y => z => _ * (x + y * z) 

그리고 :

scala> import cats.instances.option._ 
import cats.instances.option._ 

scala> val ff = liftCurriedIntoOption(f) 
ff: Option[Int] => (Option[Int] => Option[Int]) = scala.Function1$$Lambda$1744/[email protected] 

scala> val gg = liftCurriedIntoOption(g) 
gg: Option[Int] => (Option[Int] => (Option[Int] => Option[Int])) = scala.Function1$$Lambda$1744/[email protected] 

scala> val hh = liftCurriedIntoOption(h) 
hh: Option[Int] => (Option[Int] => (Option[Int] => (Option[String] => Option[String]))) = scala.Function1$$Lambda$1744/[email protected] 

지옥의 경우에도 몇 번 더 적용 할 수 있습니다.

scala> val hhhh = liftCurriedIntoOption(liftCurriedIntoOption(hh)) 
hhh: Option[Option[Option[Int]]] => (Option[Option[Option[Int]]] => (Option[Option[Option[Int]]] => (Option[Option[Option[String]]] => Option[Option[Option[String]]]))) = scala.Function1$$Lambda$1744/[email protected] 

그래서 유형 괜찮아보고, 내가 생각하는 값 ...

scala> ff(Some(1))(Some(2)) 
res0: Option[Int] = Some(3) 

scala> ff(Some(1))(None) 
res1: Option[Int] = None 

scala> hh(Some(1))(None)(None)(None) 
res2: Option[String] = None 

scala> hh(Some(1))(Some(2))(Some(3))(Some("a")) 
res3: Option[String] = Some(aaaaaaa) 

... 당신이 목표로 한 것입니다.

1

당신은 정의 할 수 있습니다 재귀 Poly

object constNone extends Poly1 { 
    implicit def zeroCase[In]: Case.Aux[In, Option[Int]] = at(_ => None) 

    implicit def succCase[In, In1, Out](implicit 
     cse: Case.Aux[In, Out]): Case.Aux[In1, In => Out] = at(_ => cse(_)) 
    } 

    object transform extends Poly1 { 
    implicit def zeroCase: Case.Aux[Int, Option[Int]] = at(Some(_)) 

    implicit def succCase[In, Out](implicit 
     cse: Case.Aux[In, Out], 
     noneCase: constNone.Case.Aux[In, Out] 
    ): Case.Aux[Int => In, Option[Int] => Out] = 
     at(f => { 
     case Some(n) => cse(f(n)) 
     case None => noneCase(f(0)) 
     }) 
    } 

    (transform((x: Int) => (y: Int) => x + y) _)(Some(1))(Some(2)) //Some(3) 
    (transform((x: Int) => (y: Int) => x + y) _)(Some(1))(None) //None 
    (transform((x: Int) => (y: Int) => x + y) _)(None)(Some(2)) //None 
+1

정말 멋졌습니다 ... 3 매개 변수 기능을 시도했을 때 분기 된대로 Lazy를 몇 가지 추가해야했습니다. –

+0

그것으로 놀고 Poly 재귀에 의해 놀란 후, 나는 아직도 알아 내야 할 2 가지가 있다는 것을 깨달았다 ... Int에 묶지 않고 Option에 묶지 않는다. –