언젠가 나는 반 신비한 표기법으로 넘어졌다.
def f[T](..) = new T[({type l[A]=SomeType[A,..]})#l] {..}
Scala 블로그 게시물에서 “우리는 그 유형 람다 트릭을 사용했습니다”핸드 웨이브를 제공합니다.
이것에 대해 약간의 직감을 가지고 있지만 (우리 A
는 정의를 오염시키지 않고 익명의 유형 매개 변수 를 얻 습니까?) 유형 람다 트릭이 무엇인지, 그 이점이 무엇인지 설명하는 명확한 출처를 찾지 못했습니다. 그것은 단지 구문 설탕입니까, 아니면 새로운 차원을 열어 줍니까?
답변
유형 람다는 종류가 높은 유형으로 작업 할 때 매우 중요합니다.
Either [A, B]의 올바른 투영을 위해 모나드를 정의하는 간단한 예를 고려하십시오. 모나드 타입 클래스는 다음과 같습니다 :
trait Monad[M[_]] {
def point[A](a: A): M[A]
def bind[A, B](m: M[A])(f: A => M[B]): M[B]
}
이제 두 인수의 형식 생성자이지만 Monad를 구현하려면 하나의 인수로 형식 생성자를 제공해야합니다. 이에 대한 해결책은 람다 유형을 사용하는 것입니다.
class EitherMonad[A] extends Monad[({type λ[α] = Either[A, α]})#λ] {
def point[B](b: B): Either[A, B]
def bind[B, C](m: Either[A, B])(f: B => Either[A, C]): Either[A, C]
}
이것은 유형 시스템에서 카레의 예입니다. EitherMonad의 인스턴스를 작성하려고 할 때 유형 중 하나를 지정하도록 Either의 유형을 카레했습니다. 물론 다른 지점은 호출 지점 또는 바인딩시 제공됩니다.
유형 람다 트릭은 유형 위치의 빈 블록이 익명 구조 유형을 생성한다는 사실을 이용합니다. 그런 다음 # 구문을 사용하여 형식 멤버를 가져옵니다.
경우에 따라 인라인을 작성하는 데 어려움이있는보다 복잡한 유형의 람다가 필요할 수 있습니다. 오늘 내 코드의 예는 다음과 같습니다.
// types X and E are defined in an enclosing scope
private[iteratee] class FG[F[_[_], _], G[_]] {
type FGA[A] = F[G, A]
type IterateeM[A] = IterateeT[X, E, FGA, A]
}
이 클래스는 독점적으로 존재하므로 FG [F, G] #IterateeM과 같은 이름을 사용하여 일부 세 번째 모나드에 특화된 두 번째 모나드의 트랜스포머 버전에 특화된 IterateeT 모나드의 유형을 참조 할 수 있습니다. 쌓기 시작할 때 이러한 종류의 구성이 매우 필요합니다. 물론 FG를 인스턴스화하지는 않습니다. 타입 시스템에서 내가 원하는 것을 표현할 수있는 해킹이 있습니다.
답변
이점은 익명 기능으로 부여 된 이점과 동일합니다.
def inc(a: Int) = a + 1; List(1, 2, 3).map(inc)
List(1, 2, 3).map(a => a + 1)
Scalaz 7의 사용 예 입니다. Functor
의 두 번째 요소에 함수를 매핑 할 수 있는를 사용하려고 합니다 Tuple2
.
type IntTuple[+A]=(Int, A)
Functor[IntTuple].map((1, 2))(a => a + 1)) // (1, 3)
Functor[({type l[a] = (Int, a)})#l].map((1, 2))(a => a + 1)) // (1, 3)
Scalaz는에 대한 유형 인수를 유추 할 수있는 암시 적 변환을 제공 Functor
하므로 종종 이러한 인수를 작성하지 마십시오. 이전 줄은 다음과 같이 다시 쓸 수 있습니다.
(1, 2).map(a => a + 1) // (1, 3)
IntelliJ를 사용하는 경우 설정, 코드 스타일, 스칼라, 접기, 유형 람다를 활성화 할 수 있습니다. 그러면 구문의 멍청한 부분 이 숨겨 지고 더 맛있어집니다.
Functor[[a]=(Int, a)].map((1, 2))(a => a + 1)) // (1, 3)
스칼라의 향후 버전은 그러한 구문을 직접 지원할 수 있습니다.
답변
상황에 맞게 :이 답변은 원래 다른 스레드에 게시되었습니다. 두 개의 스레드가 병합되었으므로 여기에서 볼 수 있습니다. 해당 스레드의 질문 내용은 다음과 같습니다.
이 유형 정의를 해결하는 방법 : Pure [({type? [a] = (R, a)}) #?]?
이러한 구성을 사용하는 이유는 무엇입니까?
Snack은 scalaz 라이브러리에서 제공됩니다.
trait Pure[P[_]] { def pure[A](a: => A): P[A] } object Pure { import Scalaz._ //... implicit def Tuple2Pure[R: Zero]: Pure[({type ?[a]=(R, a)})#?] = new Pure[({type ?[a]=(R, a)})#?] { def pure[A](a: => A) = (Ø, a) } //... }
대답:
trait Pure[P[_]] {
def pure[A](a: => A): P[A]
}
상자의 밑줄은 P
형식 생성자가 한 형식을 취하고 다른 형식을 반환한다는 것을 나타냅니다. 이 종류의 생성자의 예 : List
, Option
.
부여 List
을 Int
, 구체적인 유형을, 그리고 당신을 제공 List[Int]
, 다른 구체적인 유형입니다. 부여 List
을 String
그리고 당신을 제공합니다 List[String]
. 기타.
따라서 List
, Option
1. 공식적으로 우리가 말할 인수에 대응의 타입 수준의 기능으로 간주 될 수있다, 그들은 종류가 * -> *
. 별표는 유형을 나타냅니다.
이제 Tuple2[_, _]
종류가있는 형식 생성자입니다. (*, *) -> *
즉, 새 형식을 얻으려면 두 가지 형식을 지정해야합니다.
자신의 서명이 일치하지 않기 때문에, 당신은 대체 할 수 있습니다 Tuple2
에 대한 P
. 당신이해야 할 일은 인수 중 하나에 부분적으로 적용 Tuple2
하는 것입니다. 이는 우리에게 kind를 가진 타입 생성자를 줄 것이고 * -> *
, 우리는 그것을 대신 할 수 있습니다 P
.
불행히도 스칼라는 형식 생성자의 부분적 적용을위한 특별한 구문을 가지고 있지 않으므로 람다 형식이라는 괴물에 의존해야합니다. (당신의 예에있는 것) 그것들은 그것들이 가치 수준에 존재하는 람다 식과 유사하기 때문에 호출됩니다.
다음 예제가 도움이 될 수 있습니다.
// VALUE LEVEL
// foo has signature: (String, String) => String
scala> def foo(x: String, y: String): String = x + " " + y
foo: (x: String, y: String)String
// world wants a parameter of type String => String
scala> def world(f: String => String): String = f("world")
world: (f: String => String)String
// So we use a lambda expression that partially applies foo on one parameter
// to yield a value of type String => String
scala> world(x => foo("hello", x))
res0: String = hello world
// TYPE LEVEL
// Foo has a kind (*, *) -> *
scala> type Foo[A, B] = Map[A, B]
defined type alias Foo
// World wants a parameter of kind * -> *
scala> type World[M[_]] = M[Int]
defined type alias World
// So we use a lambda lambda that partially applies Foo on one parameter
// to yield a type of kind * -> *
scala> type X[A] = World[({ type M[A] = Foo[String, A] })#M]
defined type alias X
// Test the equality of two types. (If this compiles, it means they're equal.)
scala> implicitly[X[Int] =:= Foo[String, Int]]
res2: =:=[X[Int],Foo[String,Int]] = <function1>
편집하다:
더 많은 가치 수준 및 유형 수준 병렬.
// VALUE LEVEL
// Instead of a lambda, you can define a named function beforehand...
scala> val g: String => String = x => foo("hello", x)
g: String => String = <function1>
// ...and use it.
scala> world(g)
res3: String = hello world
// TYPE LEVEL
// Same applies at type level too.
scala> type G[A] = Foo[String, A]
defined type alias G
scala> implicitly[X =:= Foo[String, Int]]
res5: =:=[X,Foo[String,Int]] = <function1>
scala> type T = World[G]
defined type alias T
scala> implicitly[T =:= Foo[String, Int]]
res6: =:=[T,Foo[String,Int]] = <function1>
제시 한 경우, type 매개 변수 R
는 작동하기에 로컬 Tuple2Pure
이므로 type PartialTuple2[A] = Tuple2[R, A]
해당 동의어를 넣을 장소가 없기 때문에 간단히 정의 할 수 없습니다 .
이러한 경우를 처리하기 위해 형식 멤버를 사용하는 다음 트릭을 사용합니다. (이 예제는 자명하다.)
scala> type Partial2[F[_, _], A] = {
| type Get[B] = F[A, B]
| }
defined type alias Partial2
scala> implicit def Tuple2Pure[R]: Pure[Partial2[Tuple2, R]#Get] = sys.error("")
Tuple2Pure: [R]=> Pure[[B](R, B)]
답변
type World[M[_]] = M[Int]
우리에 넣어 무엇이든 그 원인 A
에 X[A]
(가) implicitly[X[A] =:= Foo[String,Int]]
항상 true I 추측이다.