복붙노트

[SCALA] HLists는 튜플을 작성하는 뒤얽힌 방식보다 더 아무것도?

SCALA

HLists는 튜플을 작성하는 뒤얽힌 방식보다 더 아무것도?

나는 차이가 더 일반적이며, 어디 HLists가 (일반 목록을 통해 어떤 혜택을 얻을 수없는, 또는 오히려)을 사용할 수 없습니다 표준 사용 사례를 식별하기 위해 알아 내기에 정말 관심이 있어요.

(한 단 하나의 HList을 필요로하는 반면 나는) 스칼라에서 TupleN (22) (내가 믿는이 있다는 것을 알고,하지만 난에 관심이 개념 차이의 종류가 아닙니다.)

나는 아래의 텍스트에서 몇 가지 질문을 표시했습니다. 실제로 그들은 더 나에게 불분명 한 가지를 지적하고, 특정 방향으로 논의를 안내하기위한 것입니다, 그들에게 대답 할 필요하지 않을 수도 있습니다.

사람들이 HLists를 사용하도록 제안 곳이 질문에 삭제 된 답변을 포함하여 (예를 들어 볼품에 의해 제공) 최근 SO에 대한 답변을 몇 보았다. 그것은 다시이 질문을 촉발이 토론에 상승했다.

당신이 정적 요소와 자신의 정확한 유형의 수를 알고있는 경우 hlists에만 유용하다는, 날 것으로 보인다. 수는 실제로 중요하지, 그러나 당신이 이제까지 다양한 요소이지만 정적으로 정확하게 알려진 유형의 목록을 생성 할 필요가 없을 것 같다,하지만 당신은 정적으로 자신의 번호를 알고하지 않습니다. 질문 1 : 당신은 심지어 루프에서, 예를 들어 같은 예를 들어, 쓰기 수 있을까요? 내 직감은 임의의 요소의 정적으로 알 수없는 번호 (주어진 클래스 계층 구조에 임의의 상대)와 정적으로 정확한 hlist을 가진 것은 단지 호환되지 않는 것입니다.

이것이 사실이라면, 즉, 당신은 정적 번호를 알고 입력 - 질문 2 : 왜 그냥하는 n 튜플을 사용합니까? 물론, 당신은 typesafely 매핑 할 수 있습니다와 HList을 통해 배 (이 또한 수 있지만 typesafely, productIterator의 도움으로 튜플을 통해 할),하지만 정적으로 당신은 아마 그냥 튜플에 액세스 할 수 알려진 요소의 수와 유형부터 요소는 직접 조작을 수행한다.

당신 F 함수가 hlist을 이상하게되면, 다른 한편으로, 모든 요소를 ​​받아들이는 너무 일반적입니다 - 질문 3 : 왜 안 productIterator.map를 통해 그것을 사용합니까? 좋아, 한 가지 흥미로운 차이점은 메소드 오버로딩에서 올 수 : 우리는 몇 가지 오버로드 (F)의이 있다면, 컴파일러는보다 구체적인 F를 선택하도록 허용 할 수 있습니다합니다 (productIterator 달리)에 hlist가 제공하는 강력한 타입 정보를 가지고. 그러나, 나는 방법과 기능이 동일하지 않기 때문에 그 사실 스칼라에서 일 것입니다하지 않도록 경우입니다.

요소가 사용자 상호 작용의 종류에 따라 다릅니다 곳 hlists이 상황에서 사용할 수 : 질문 4 - 당신이 정적 요소의 수와 유형을 알 필요가 있음을, 즉, 같은 가정에 구축? 예를 들어, 내부 루프 요소와 hlist 채우는 상상; 요소는 대체로 (UI, 구성 파일, 배우 상호 작용 네트워크) 특정 조건에서 유지 될 때까지 판독된다. hlist의 유형은 어떤 것입니까? 인터페이스 사양 getElements 유사 : HList [...] 즉 정적 알 길이리스트와 함께 작동해야하고, 그 구성 요소 B.에서 임의의 그러한리스트를 얻는 시스템에서 A 성분을 허용

해결법

  1. ==============================

    1.주소 질문에 1 ~ 3 : HLists의 주요 응용 프로그램 중 하나는 인수에 대응을 통해 추출된다. 인수에 대응은 일반적으로 정적 추상화의 특정 사용하는 사이트에 알려져 있지만 사이트마다 다양합니다. 무형의 예에서,이 걸릴,

    주소 질문에 1 ~ 3 : HLists의 주요 응용 프로그램 중 하나는 인수에 대응을 통해 추출된다. 인수에 대응은 일반적으로 정적 추상화의 특정 사용하는 사이트에 알려져 있지만 사이트마다 다양합니다. 무형의 예에서,이 걸릴,

    def flatten[T <: Product, L <: HList](t : T)
      (implicit hl : HListerAux[T, L], flatten : Flatten[L]) : flatten.Out =
        flatten(hl(t))
    
    val t1 = (1, ((2, 3), 4))
    val f1 = flatten(t1)     // Inferred type is Int :: Int :: Int :: Int :: HNil
    val l1 = f1.toList       // Inferred type is List[Int]
    
    val t2 = (23, ((true, 2.0, "foo"), "bar"), (13, false))
    val f2 = flatten(t2)
    val t2b = f2.tupled
    // Inferred type of t2b is (Int, Boolean, Double, String, String, Int, Boolean)
    

    평평하게 튜플 인수의 인수에 대응을 통해 추상적으로 HLists (또는 뭔가 해당)를 사용하지 않고는이 두 가지 매우 다른 형태의 인수를 승인하고 유형 안전한 방법으로 그들을 변환 할 수있는 하나의 구현이 불가능하다.

    뿐만 아니라 튜플을 위와 같이, 방법 / 함수 매개 변수 목록 및 케이스 클래스를 포함합니다 : 추상을 통해 인수에 대응하는 능력은 가능성이 고정 arities이 참여하고 관심의 어느 곳이 될 것입니다. 예 여기를 참조하십시오 어떻게 거의 자동으로 입력 클래스 인스턴스를 얻기 위해 임의의 경우 클래스의 인수에 대응 위로 추상 수도,

    // A pair of arbitrary case classes
    case class Foo(i : Int, s : String)
    case class Bar(b : Boolean, s : String, d : Double)
    
    // Publish their `HListIso`'s
    implicit def fooIso = Iso.hlist(Foo.apply _, Foo.unapply _)
    implicit def barIso = Iso.hlist(Bar.apply _, Bar.unapply _)
    
    // And now they're monoids ...
    
    implicitly[Monoid[Foo]]
    val f = Foo(13, "foo") |+| Foo(23, "bar")
    assert(f == Foo(36, "foobar"))
    
    implicitly[Monoid[Bar]]
    val b = Bar(true, "foo", 1.0) |+| Bar(false, "bar", 3.0)
    assert(b == Bar(true, "foobar", 4.0))
    

    어떤 런타임 반복은 여기에 없다,하지만 HLists (또는 이에 상응하는 구조)의 사용을 제거 할 수있는 중복이있다. 반복적 인 상용구에 대한 내성이 높은 경우 물론, 당신은 각각의 모든 당신이 걱정하는 것을 모양에 대해 여러 구현을 작성하여 동일한 결과를 얻을 수 있습니다.

    질문 세 가지에 물어 "... 당신 F 함수가 왜 productIterator.map를 통해 그것을 사용하지 ... hlist이 모든 요소를 ​​받아들이는 너무 일반적이기 이상하게되면,?". 당신이 HList을 통해 매핑 기능이 정말 형태로 모든 => T 인 경우 다음 productIterator을 통해 매핑하는 완벽하게 서비스를 제공합니다. 그러나 형태로 모든 => T의 기능은 흥미가 (그들이 내부적으로 캐스팅 입력하지 않으면 적어도, 그렇지 않은) 것을 일반적으로하지 않습니다. 볼품은 컴파일러가있는 거 의심에 대해 정확히 방법 당신의 유형 특정 케이스를 선택할 수 있습니다 다형성 함수 값의 양식을 제공합니다. 예를 들어,

    // size is a function from values of arbitrary type to a 'size' which is
    // defined via type specific cases
    object size extends Poly1 {
      implicit def default[T] = at[T](t => 1)
      implicit def caseString = at[String](_.length)
      implicit def caseList[T] = at[List[T]](_.length)
    }
    
    scala> val l = 23 :: "foo" :: List('a', 'b') :: true :: HNil
    l: Int :: String :: List[Char] :: Boolean :: HNil =
      23 :: foo :: List(a, b) :: true :: HNil
    
    scala> (l map size).toList
    res1: List[Int] = List(1, 3, 2, 1)
    

    사용자 입력에 대한 질문 사,과 관련하여 고려해야 할 두 가지 경우가 있습니다. 먼저 우리가 동적으로 알려진 정적 상태를 얻을 보장하는 컨텍스트를 설정할 수있는 상황이다. 시나리오 이러한 종류의 그것은하지만 분명히 정적 상태가 런타임에 얻을하지 않는 경우 우리는 다른 경로를 따라해야한다는 것을 조건으로, 무형의 기술을 적용 할 수 있도록 완벽하게 가능합니다. 당연히,이 수단은 동적 조건에 민감한 방법이 선택 결과를 얻을 필요가있다. 여기 HLists를 사용하는 예입니다,

    trait Fruit
    case class Apple() extends Fruit
    case class Pear() extends Fruit
    
    type FFFF = Fruit :: Fruit :: Fruit :: Fruit :: HNil
    type APAP = Apple :: Pear :: Apple :: Pear :: HNil
    
    val a : Apple = Apple()
    val p : Pear = Pear()
    
    val l = List(a, p, a, p) // Inferred type is List[Fruit]
    

    리터의 유형은리스트의 길이, 또는 그 요소의 정확한 유형을 캡처하지 않습니다. 그러나, 우리가 특정 양식을 가지고 (예. 몇 가지 알려진 고정 된 스키마에 부합하도록 해야지 경우), 우리는 그 사실을 수립하고 그에 따라 행동을 시도 할 수 예상되는 경우,

    scala> import Traversables._
    import Traversables._
    
    scala> val apap = l.toHList[Apple :: Pear :: Apple :: Pear :: HNil]
    res0: Option[Apple :: Pear :: Apple :: Pear :: HNil] =
      Some(Apple() :: Pear() :: Apple() :: Pear() :: HNil)
    
    scala> apap.map(_.tail.head)
    res1: Option[Pear] = Some(Pear())
    

    그것은 다른 목록과 같은 길이입니다 이외의 우리는 주어진리스트의 실제 길이 걱정하지 않을 수 있습니다 다른 상황이있다. 다시 말하지만,이 뭔가 그 완전히 정적, 또한 상기와 혼합 정적 / 동적 상황에서 모두 무형의 지원은. 확장 예를 들어 여기를 참조하십시오.

    이 관찰은 이러한 메커니즘의 모든 적어도 조건부 사용할 수 정적 유형 정보를 요구하고, 즉, 완전히 외부에서 제공 형식화되지 않은 데이터에 의해 구동되는 완전히 동적 인 환경에서 사용할 수있는에서 이러한 기술을 제외 보일 것이라고, 사실이다. 그러나 2.10의 스칼라 반사의 구성 요소와 같은 런타임 컴파일에 대한 지원의 출현도이는 극복하기 어려운 장애물은 더 이상 함께 ... 우리는 가벼운 준비의 양식을 제공하고 우리의 정적 타이핑 런타임에서 수행하도록 런타임 컴파일을 사용할 수 있습니다 동적 데이터에 대한 응답 : 아래 앞에서 발췌 ... 전체 예를 들어 링크를 따라,

    val t1 : (Any, Any) = (23, "foo") // Specific element types erased
    val t2 : (Any, Any) = (true, 2.0) // Specific element types erased
    
    // Type class instances selected on static type at runtime!
    val c1 = stagedConsumeTuple(t1) // Uses intString instance
    assert(c1 == "23foo")
    
    val c2 = stagedConsumeTuple(t2) // Uses booleanDouble instance
    assert(c2 == "+2.0")
    

    나는 의존적으로 입력 된 프로그래밍 언어에 대한 자신의 현자 의견을 주어, @PLT_Borat 그것에 대해 할 말이있을 것이라 확신합니다 ;-)

  2. ==============================

    2.그냥 HList 본질적 위에 약간 다른 설탕 Tuple2의 스택에 불과하다, 명확해야합니다.

    그냥 HList 본질적 위에 약간 다른 설탕 Tuple2의 스택에 불과하다, 명확해야합니다.

    def hcons[A,B](head : A, tail : B) = (a,b)
    def hnil = Unit
    
    hcons("foo", hcons(3, hnil)) : (String, (Int, Unit))
    

    귀하의 질문에 평면 튜플 대 중첩 된 튜플을 사용 사이의 차이점에 대해 기본적이지만, 두 동형이다 그래서 그래서 결국 정말 라이브러리 함수를 사용할 수 있으며, 어떤 표기하여 사용할 수있는 편리 성을 제외하고 차이가 없습니다.

  3. ==============================

    3.튜플에 당신이 (잘) 할 수없는 것들이 많이 있습니다 :

    튜플에 당신이 (잘) 할 수없는 것들이 많이 있습니다 :

    당신은하지만 일반적인 경우에, 물론 튜플로이 모든 것을 할 수 있습니다. HLists를 사용하여 코드를 더 건조하게 그래서.

  4. ==============================

    4.나는 슈퍼 간단한 언어로 이것을 설명 할 수 있습니다 :

    나는 슈퍼 간단한 언어로 이것을 설명 할 수 있습니다 :

    목록의 이름을 대 튜플은 중요하지 않습니다. HLists은 HTuples로 명명 될 수있다. 차이점은 스칼라 + 하스켈, 당신은 튜플 (스칼라 구문을 사용)이 작업을 수행 할 수 있다는 것입니다 :

    def append2[A,B,C](in: (A,B), v: C) : (A,B,C) = (in._1, in._2, v)
    

    모든 유형의 정확히 두 요소의 입력 튜플을 위해, 세 번째 요소를 추가하고, 정확히 3 요소를 완전히 입력 튜플을 반환합니다. 이 유형을 통해 완전히 일반적인 반면, 그것은 명시 적으로 입력 / 출력 길이를 지정할 수 있습니다.

    하스켈 스타일 HList는 튜플 / 목록의 길이에 추가하고 완전히 정적으로 입력 된 튜플 / 목록을 다시 얻을 수 있도록,이 일반적인 이상 길이을 당신이 수행 할 수 있습니다 무엇. 이 혜택은 정확히 N INT의 목록에 int를 추가 명시 적으로 n은 지정하지 않고 정확히하도록하는 int를 (1 N +)를 정적으로 입력 된 목록을 다시 얻을 수 있습니다 균일하게 입력 컬렉션에 적용됩니다.

  5. from https://stackoverflow.com/questions/11825129/are-hlists-nothing-more-than-a-convoluted-way-of-writing-tuples by cc-by-sa and MIT license