더 높은 종류의 유형은 언제 유용합니까?


88

나는 한동안 F #에서 개발을 해왔고 그것을 좋아합니다. 그러나 내가 아는 한 가지 유행어는 F #에 존재하지 않는 고급 유형입니다. 나는 고급 유형에 대한 자료를 읽었으며 그 정의를 이해한다고 생각합니다. 나는 그들이 왜 유용한 지 잘 모르겠습니다. 누군가 Scala 또는 Haskell에서 F #의 해결 방법이 필요한 고급 유형의 예를 제공 할 수 있습니까? 또한 이러한 예제의 경우 더 높은 종류의 유형이 없으면 해결 방법이 무엇입니까 (또는 F #의 경우 그 반대)? 어쩌면 나는 그 문제를 해결하는 데 너무 익숙해서 그 기능이 없다는 것을 알지 못합니다.

(나는 생각한다) 대신 myList |> List.map f또는 myList |> Seq.map f |> Seq.toList더 높은 종류의 유형을 사용하면 간단하게 작성할 myList |> map f수 있으며 List. 그것은 훌륭하지만 (정확하다고 가정) 약간 사소한 것 같습니까? (그리고 단순히 함수 오버로딩을 허용하는 것만으로 할 수는 없나요?) 저는 보통 Seq어쨌든 로 변환 한 다음 나중에 원하는대로 변환 할 수 있습니다. 다시 말하지만, 나는 그것을 해결하는 데 너무 익숙 할 것입니다. 그러나 더 높은 종류의 유형 키 입력이나 유형 안전에서 실제로 당신을 구하는 예가 있습니까?


2
Control.Monad의 많은 함수 는 더 높은 종류를 사용하므로 몇 가지 예를 찾아 볼 수 있습니다. F #에서는 각 구체적인 모나드 유형에 대해 구현을 반복해야합니다.
Lee

1
@Lee하지만 인터페이스 IMonad<T>를 만든 다음 예를 들어 IEnumerable<int>또는 IObservable<int>완료되면 다시 캐스팅 할 수 없습니까? 이 모든 것이 캐스팅을 피하기위한 것입니까?
lobsterism

4
Well Casting은 안전하지 않으므로 형식 안전성에 대한 질문에 답합니다. 또 다른 문제는 return그것이 실제로는 특정 인스턴스가 아닌 모나드 유형에 속하므로 IMonad인터페이스에 전혀 넣지 않기 때문에 어떻게 작동 하는지 입니다 .
Lee

4
@Lee yeah 나는 표현 후에 최종 결과를 캐스팅해야 할 것이라고 생각하고 있었는데, 그냥 표현을해서 타입을 알기 때문에 별거 아니야. 그러나 bind일명 SelectMany등의 각 impl 내부에도 캐스팅해야 할 것 같습니다 . 누군가에게 API를 사용할 수 있습니다 의미합니다 로 하고, 그것을 작동합니다 생각하는 그런 경우와 방법은 그 주위에 존재하지 않는 경우는 우웩 그래. 주변에 방법이 없다고 100 % 확신하지는 않습니다. bindIObservableIEnumerable
lobsterism

5
좋은 질문입니다. 이 언어 기능이 유용한 IRL이라는 강력한 실용적인 예는 아직 보지 못했습니다.
JD

답변:


78

따라서 유형의 종류는 단순한 유형입니다. 예를 들어 기본 유형이며 값으로 인스턴스화 할 수 있음을 의미하는 Int종류 *가 있습니다. 더 높은 종류의 유형에 대한 느슨한 정의 (그리고 F #이 선을 그리는 위치가 확실하지 않으므로 포함하겠습니다) 다형성 컨테이너 는 더 높은 종류의 유형의 좋은 예입니다.

data List a = Cons a (List a) | Nil

타입 생성자는 List종류가 * -> *: 그것은 구체적인 유형을 초래할하기 위해 구체적인 유형을 전달해야하는 수단 List Int등의 주민을 가질 수 [1,2,3]있지만 List그 자체가 없습니다.

다형성 컨테이너의 이점은 분명하지만 컨테이너보다 더 유용한 종류의 * -> *유형이 존재 한다고 가정하겠습니다 . 예를 들어, 관계

data Rel a = Rel (a -> a -> Bool)

또는 파서

data Parser a = Parser (String -> [(a, String)])

둘 다 종류도 있습니다 * -> *.


그러나 우리는 더 높은 순서의 종류를 가진 타입을 가짐으로써 Haskell에서 이것을 더 나아갈 수 있습니다. 예를 들어 우리는 kind를 가진 유형을 찾을 수 (* -> *) -> *있습니다. 이것의 간단한 예 Shape는 종류의 컨테이너를 채우려 고 시도하는 것일 수 있습니다 * -> *.

data Shape f = Shape (f ())

[(), (), ()] :: Shape List

Traversable예를 들어 Haskell에서 s 를 특성화하는 데 유용합니다. 예를 들어 항상 모양과 내용으로 나눌 수 있기 때문입니다.

split :: Traversable t => t a -> (Shape t, [a])

또 다른 예로, 가지고있는 가지의 종류에 따라 매개 변수화 된 트리를 생각해 봅시다. 예를 들어, 일반 나무는

data Tree a = Branch (Tree a) a (Tree a) | Leaf

하지만 분기 유형에 a Pairof Tree as 가 포함되어 있으므로 해당 유형에서 매개 변수로 해당 부분을 추출 할 수 있습니다.

data TreeG f a = Branch a (f (TreeG f a)) | Leaf

data Pair a = Pair a a
type Tree a = TreeG Pair a

TreeG유형 생성자는 kind가 (* -> *) -> * -> *있습니다. 우리는 그것을 사용하여 다음과 같은 흥미로운 다른 변형을 만들 수 있습니다.RoseTree

type RoseTree a = TreeG [] a

rose :: RoseTree Int
rose = Branch 3 [Branch 2 [Leaf, Leaf], Leaf, Branch 4 [Branch 4 []]]

또는 병리학적인 것 MaybeTree

data Empty a = Empty
type MaybeTree a = TreeG Empty a

nothing :: MaybeTree a
nothing = Leaf

just :: a -> MaybeTree a
just a = Branch a Empty

또는 TreeTree

type TreeTree a = TreeG Tree a

treetree :: TreeTree Int
treetree = Branch 3 (Branch Leaf (Pair Leaf Leaf))

이것이 나타나는 또 다른 장소는 "펑터의 대수"입니다. 추상화 레이어를 몇 개 삭제하면 sum :: [Int] -> Int. 대수는 펑터캐리어에 대해 매개 변수화 됩니다. 펑은 종류가 * -> *캐리어 종류 *그래서 모두

data Alg f a = Alg (f a -> a)

종류가 (* -> *) -> * -> *있습니다. Alg데이터 유형과 그 위에 구축 된 재귀 체계와의 관계 때문에 유용합니다.

-- | The "single-layer of an expression" functor has kind `(* -> *)`
data ExpF x = Lit Int
            | Add x x
            | Sub x x
            | Mult x x

-- | The fixed point of a functor has kind `(* -> *) -> *`
data Fix f = Fix (f (Fix f))

type Exp = Fix ExpF

exp :: Exp
exp = Fix (Add (Fix (Lit 3)) (Fix (Lit 4))) -- 3 + 4

fold :: Functor f => Alg f a -> Fix f -> a
fold (Alg phi) (Fix f) = phi (fmap (fold (Alg phi)) f)

그들은 이론적으로는 가능이야하지만 마지막으로, 나는 못 볼 것 높은 kinded 타입의 생성자를. 우리는 때때로와 같은 유형의 함수를 볼 수 mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b있지만 유형의 복잡성 수준을 보려면 유형 프롤로그 또는 종속 유형의 문헌을 파헤쳐 야한다고 생각합니다.


3
몇 분 안에 코드를 입력 확인하고 수정하겠습니다. 지금 바로 전화를 사용하고 있습니다.
J. Abrahamson 2014 년

12
@ J.Abrahamson +1 좋은 답변과 인내심을 가지고 휴대 전화에 입력 O_o
Daniel Gratzer

3
@lobsterism A TreeTree는 단지 병리 적이지만 실제로는 두 가지 유형의 나무가 서로 얽혀 있음을 의미합니다.이 아이디어를 조금 더 밀면 정적으로 안전한 빨간색과 같은 매우 강력한 유형 안전 개념을 얻을 수 있습니다. 검은 나무와 깔끔한 정적으로 균형 잡힌 FingerTree 유형.
J. Abrahamson 2014 년

3
@JonHarrop 표준 실제 예제는 mtl 스타일 효과 스택과 같은 모나드를 추상화하는 것입니다. 그러나 이것이 실제 세계에서 가치가 있다는 데 동의하지 않을 수 있습니다. HKT 없이도 언어가 성공적으로 존재할 수 있다는 것이 일반적으로 분명하다고 생각합니다. 따라서 어떤 예도 다른 언어보다 더 정교한 추상화를 제공 할 것입니다.
J. Abrahamson

2
예를 들어 다양한 모나드에서 인증 된 효과의 하위 집합을 가질 수 있으며 해당 사양을 충족하는 모나드에 대해 추상화 할 수 있습니다. 예를 들어, 문자 수준 읽기 및 쓰기를 가능하게하는 "teletype"을 인스턴스화하는 모나드는 IO 및 파이프 추상화를 모두 포함 할 수 있습니다. 다른 예로 다양한 비동기 구현을 추상화 할 수 있습니다. HKT가 없으면 해당 일반 조각으로 구성된 모든 유형을 제한합니다.
J. Abrahamson 2015 년

64

더 높은 종류의 유형 변수가있는 FunctorHaskell 의 유형 클래스를 고려하십시오 f.

class Functor f where
    fmap :: (a -> b) -> f a -> f b

이 유형 서명이 말하는 것은 fmap이의 유형 매개 변수를 ffrom a으로 변경 b하지만 f그대로 유지한다는 것입니다. 따라서 fmap목록을 통해 사용 하면 목록을 얻을 수 있고 파서를 통해 사용하면 구문 분석기를 얻게되는 식입니다. 그리고 이것들은 정적입니다 , 컴파일 타임 보장입니다.

저는 F #을 모릅니다.하지만 Functor상속과 제네릭을 사용하여 더 높은 종류의 제네릭이없는 Java 또는 C #과 같은 언어로 추상화 를 표현하려고하면 어떤 일이 발생하는지 살펴 보겠습니다 . 첫 시도:

interface Functor<A> {
    Functor<B> map(Function<A, B> f);
}

이 첫 번째 시도의 문제점은 인터페이스 구현이 를 구현하는 모든 클래스 를 반환 할 수 있다는 것 Functor입니다. 누군가는 FunnyList<A> implements Functor<A>map메서드가 다른 종류의 컬렉션을 반환하거나 컬렉션이 아니지만 여전히 Functor. 또한 map메서드 를 사용할 때 실제로 예상하는 유형으로 다운 캐스트하지 않는 한 결과에 대해 하위 유형별 메서드를 호출 할 수 없습니다. 따라서 두 가지 문제가 있습니다.

  1. 유형 시스템은 map메서드가 항상 Functor수신자 와 동일한 하위 클래스를 반환 한다는 불변성을 표현할 수 없습니다 .
  2. 따라서 Functor의 결과에 대해 비 메서드 를 호출하는 정적으로 형식이 안전한 방식 은 없습니다 map.

시도 할 수있는 다른 더 복잡한 방법이 있지만 실제로 작동하는 방법은 없습니다. 예를 들어, Functor결과 유형을 제한 하는 하위 유형을 정의하여 첫 번째 시도를 보강 할 수 있습니다.

interface Collection<A> extends Functor<A> {
    Collection<B> map(Function<A, B> f);
}

interface List<A> extends Collection<A> {
    List<B> map(Function<A, B> f);
}

interface Set<A> extends Collection<A> {
    Set<B> map(Function<A, B> f);
}

interface Parser<A> extends Functor<A> {
    Parser<B> map(Function<A, B> f);
}

// …

이는 좁은 인터페이스의 구현자가 메서드 Functor에서 잘못된 유형을 반환하는 것을 방지하는 데 도움이 map되지만, Functor구현할 수있는 구현 수에 제한이 없기 때문에 필요한 더 좁은 인터페이스 수에는 제한이 없습니다.

( 편집 : 그리고 이것은 Functor<B>결과 유형으로 나타나기 때문에 작동하므로 자식 인터페이스가 범위를 좁힐 수 있습니다. 따라서 AFAIK는 Monad<B>다음 인터페이스에서 두 가지 용도를 모두 좁힐 수 없습니다 .

interface Monad<A> {
    <B> Monad<B> flatMap(Function<? super A, ? extends Monad<? extends B>> f);
}

Haskell에서 상위 유형 변수를 사용하는 경우 이것은입니다 (>>=) :: Monad m => m a -> (a -> m b) -> m b.)

또 다른 시도는 재귀 제네릭을 사용하여 인터페이스가 하위 유형의 결과 유형을 하위 유형 자체로 제한하도록하는 것입니다. 장난감 예 :

/**
 * A semigroup is a type with a binary associative operation.  Law:
 *
 * > x.append(y).append(z) = x.append(y.append(z))
 */
interface Semigroup<T extends Semigroup<T>> {
    T append(T arg);
}

class Foo implements Semigroup<Foo> {
    // Since this implements Semigroup<Foo>, now this method must accept 
    // a Foo argument and return a Foo result. 
    Foo append(Foo arg);
}

class Bar implements Semigroup<Bar> {
    // Any of these is a compilation error:

    Semigroup<Bar> append(Semigroup<Bar> arg);

    Semigroup<Foo> append(Bar arg);

    Semigroup append(Bar arg);

    Foo append(Bar arg);

}

그러나 이런 종류의 기술 (당신의 평범한 OOP 개발자에게는 다소 비현실적이며 평범한 기능 개발자에게는 도대체)은 여전히 ​​원하는 Functor제약을 표현할 수 없습니다 .

interface Functor<FA extends Functor<FA, A>, A> {
    <FB extends Functor<FB, B>, B> FB map(Function<A, B> f);
}

여기서 문제는이 제한하지 않는 것입니다 FB같은 가지고 F같은 FA당신이 유형을 선언 할 때 - 그럼 List<A> implements Functor<List<A>, A>map방법은 수 여전히 을 반환을 NotAList<B> implements Functor<NotAList<B>, B>.

원시 유형 (매개 변수화되지 않은 컨테이너)을 사용하여 Java에서 마지막 시도 :

interface FunctorStrategy<F> {
    F map(Function f, F arg);
} 

여기에서 Fjust List또는 같은 매개 변수화되지 않은 유형으로 인스턴스화됩니다 Map. 이것은 FunctorStrategy<List>a가List 있지만 목록의 요소 유형을 추적하는 데 유형 변수 사용을 포기했습니다.

여기서 문제의 핵심은 Java 및 C #과 같은 언어가 유형 매개 변수가 매개 변수를 갖는 것을 허용하지 않는다는 것입니다. 경우 자바에서 T형태 변수는, 당신은 쓸 수 TList<T>하지만, T<String>. 더 높은 종류의 유형은이 제한을 제거하므로 다음과 같은 것을 가질 수 있습니다 (완전히 고려되지 않음).

interface Functor<F, A> {
    <B> F<B> map(Function<A, B> f);
}

class List<A> implements Functor<List, A> {

    // Since F := List, F<B> := List<B>
    <B> List<B> map(Function<A, B> f) {
        // ...
    }

}

특히이 비트를 처리합니다.

(나는 생각한다) 대신 myList |> List.map f또는 myList |> Seq.map f |> Seq.toList더 높은 종류의 유형을 사용하면 간단하게 작성할 myList |> map f수 있으며 List. 훌륭하지만 (정확하다고 가정 할 때) 약간 사소한 것 같나요? (그리고 단순히 함수 오버로딩을 허용하는 것만으로 할 수는 없나요?) 저는 보통로 변환 Seq한 다음 나중에 원하는대로 변환 할 수 있습니다.

map이러한 방식으로 함수 의 개념을 일반화하는 많은 언어가 있습니다. 마치 매핑이 시퀀스에 관한 것처럼 모델링함으로써. 이 발언은 그 정신입니다.에서 변환을 지원하는 유형이있는 경우를 Seq재사용하여 "무료"로지도 작업을 수행 할 수 Seq.map있습니다.

그러나 Haskell에서는 Functor클래스가 그것보다 더 일반적입니다. 그것은 시퀀스의 개념에 묶여 있지 않습니다. 액션, 파서 결합 자, 함수 fmap등과 같이 시퀀스에 대한 좋은 매핑이없는 유형에 대해 구현할 수 있습니다 IO.

instance Functor IO where
    fmap f action =
        do x <- action
           return (f x)

 -- This declaration is just to make things easier to read for non-Haskellers 
newtype Function a b = Function (a -> b)

instance Functor (Function a) where
    fmap f (Function g) = Function (f . g)  -- `.` is function composition

"매핑"의 개념은 실제로 시퀀스와 관련이 없습니다. 펑터 법칙을 이해하는 것이 가장 좋습니다.

(1) fmap id xs == xs
(2) fmap f (fmap g xs) = fmap (f . g) xs

매우 비공식적으로 :

  1. 첫 번째 법칙은 identity / noop 함수를 사용한 매핑이 아무것도하지 않는 것과 같다고 말합니다.
  2. 두 번째 법칙은 두 번 매핑하여 생성 할 수있는 결과를 한 번 매핑하여 생성 할 수도 있다는 것입니다.

이것이 fmap유형을 보존 하려는 이유 map입니다. 다른 결과 유형을 생성하는 연산을 받는 즉시 이와 같은 보장을하기가 훨씬 더 어려워지기 때문입니다.


나는 당신의 마지막 비트에 관심이 있어요 그래서, 왜 그것은이 가지고 유용 fmapFunction a이미 그것을 가질 때 .작업을? 나는 왜 op .의 정의가 타당하다는 것을 이해 fmap하지만 . fmap대신 사용할 필요가있는 곳을 얻지 못했습니다 .. 유용 할만한 예를 들어 보면 이해하는 데 도움이 될 것입니다.
lobsterism

1
아, 그것을 가지고 : 당신이 FN 할 수 double펑터의 double [1, 2, 3]제공 [2, 4, 6]double sin이중 죄 인 FN을 제공합니다. 그 사고 방식에서 생각하기 시작하면 배열에서 맵을 실행할 때 시퀀스가 ​​아닌 배열을 기대할 수 있습니다. 왜냐하면 여기서 배열 작업을하고 있기 때문입니다.
lobsterism

@lobsterism : a를 추상화 Functor하고 라이브러리의 클라이언트가 선택하도록하는 알고리즘 / 기술이 있습니다. J. Abrahamson의 답변은 한 가지 예를 제공합니다. 재귀 폴드는 펑터를 사용하여 일반화 할 수 있습니다. 또 다른 예는 무료 모나드입니다. 이것들은 일종의 일반 인터프리터 구현 라이브러리로 생각할 수 있습니다. 여기서 클라이언트는 "명령어 세트"를 임의의 Functor.
Luis Casillas 2014 년

3
기술적으로 건전한 대답이지만 왜 누군가가 이것을 실제로 원하는지 궁금해합니다. 나는 Haskell Functor이나 SemiGroup. 실제 프로그램이이 언어 기능을 가장 많이 사용하는 곳은 어디입니까?
JD

28

이미 여기에있는 훌륭한 답변에 대한 정보를 반복하고 싶지는 않지만 추가하고 싶은 핵심 사항이 있습니다.

일반적으로 특정 모나드 또는 펑터 (또는 응용 펑터, 화살표 또는 ...)를 구현하기 위해 더 높은 종류의 유형이 필요하지 않습니다. 그러나 그렇게하는 것은 대부분 요점을 놓치고 있습니다.

일반적으로 나는 사람들이 functors / monads / whatevers의 유용성을 보지 못할 때 종종 이러한 것들을 한 번에 하나씩 생각하기 때문이라는 것을 발견 했습니다 . Functor / monad / etc 작업은 실제로 하나의 인스턴스에 아무것도 추가하지 않습니다 (bind, fmap 등을 호출하는 대신 bind, fmap 등을 구현 하는 데 사용한 모든 작업을 호출 할 수 있음 ). 이러한 추상화를 정말로 원하는 것은 모든 functor / monad / etc에서 일반적으로 작동하는 코드를 가질 수 있도록하는 것 입니다.

이러한 일반 코드가 널리 사용되는 상황에서 이는 새 모나드 인스턴스를 작성할 때마다 유형 이 이미 작성된 많은 유용한 작업에 즉시 액세스 할 수 있음을 의미합니다 . 그것이 어디에서나 모나드 (그리고 펑터, ...)를 보는 요점입니다. 아니 내가 사용할 수있는 bind것보다 concatmap구현 myFunkyListOperation(나에게 그 자체로 아무것도 얻는없는), 오히려 내가 필요에 올 때 myFunkyParserOperationmyFunkyIOOperation는 제네릭 모나드 실제로 있기 때문에 내가 할 수있는 목록의 관점에서 코드 나는 원래 톱을 다시 사용 .

그러나 type safety가 있는 모나드 같은 매개 변수화 된 유형을 추상화 하려면 더 높은 종류의 유형이 필요합니다 (여기에 다른 답변에서 잘 설명되어 있습니다).


9
이것은 지금까지 읽은 다른 답변보다 유용한 답변에 가깝지만 더 높은 종류가 유용한 단일 실용적인 응용 프로그램을 계속보고 싶습니다.
JD

"당신이 정말로 원하는 것은 모든 펑터 / 모나드에서 일반적으로 작동하는 코드를 가질 수 있도록하기 위해서입니다." F #은 13 년 전에 계산 표현식의 형태로 모나드를 얻었으며 원래는 seq 및 비동기 모나드를 사용했습니다. 오늘날 F #은 세 번째 모나드 인 쿼리를 사용합니다. 공통점이 거의없는 모나드가 거의 없는데 왜 그것들을 추상화하고 싶습니까?
JD

@JonHarrop 다른 사람들이 HKT를 지원하는 언어로 엄청난 수의 모나드 (및 펑터, 화살표 등; HKT는 모나드에 관한 것이 아님)를 사용하여 코드를 작성하고이를 추상화하는 용도를 찾았다는 것을 분명히 알고 있습니다. 그리고 분명히 당신은 그 코드 중 어느 것도 실용적인 용도가 없다고 생각하고 다른 사람들이 왜 그것을 작성하려고하는지 궁금합니다. 5 년 전에 이미 댓글을 단 6 년 된 게시물에 대한 토론을 시작하기 위해 돌아와서 어떤 종류의 통찰력을 얻고 싶습니까?
Ben

"6 년 된 게시물에 대한 토론을 시작하기 위해 돌아와서 이득을 얻고 싶다". 회고전. 돌이켜 보면 이제 모나드에 대한 F #의 추상화가 거의 사용되지 않는다는 것을 알 수 있습니다. 따라서 크게 다른 3 가지를 추상화하는 능력은 설득력이 없습니다.
JD

@JonHarrop 내 대답의 요점은 개별 모나드 (또는 펑터 등)가 유목 인터페이스없이 표현 된 유사한 기능보다 실제로 더 유용하지는 않지만 많은 이질적인 것들을 통합한다는 것입니다. F #에 대한 귀하의 전문 지식을 따르겠습니다. 그러나 실패, 상태 저장, 구문 분석 등과 같은 하나를 가질 수있는 모든 개념에 대해 모나드 인터페이스를 구현하는 대신 3 개의 개별 모나드 만 있다고 말하는 경우 예,이 세 가지를 통합하여 많은 이점을 얻지 못한다는 것은 놀라운 일이 아닙니다.
Ben

15

좀 더 .NET 관련 관점에서 나는 이것에 대한 블로그 게시물을 얼마 전에 썼습니다 . 핵심은 더 높은 종류의 유형을 사용하면 잠재적으로 IEnumerables와 사이에 동일한 LINQ 블록을 재사용 할 수 있다는 것입니다.IObservables 있지만 더 높은 종류의 유형이 없으면 불가능합니다.

당신이 (필자는 블로그를 게시 한 후 알아 낸) 얻을 수있는 가장 가까운 당신의 자신을 만드는 것입니다 IEnumerable<T>IObservable<T>과에서 둘 다 확장 IMonad<T>. 이것은 그들이 표시하는 경우 당신은 당신의 LINQ 블록을 재사용 할 수 있도록 것입니다 IMonad<T>하지만 수 있기 때문에 다음 더 이상 형태 보증의 당신은 믹스 앤 매치를하는 방법 IObservablesIEnumerables 그것을, 당신은 좋겠이 가능하도록주고 싶었어요 사운드 수 있지만 같은 블록 내에서 기본적으로 정의되지 않은 동작이 발생합니다.

나는 Haskell이 이것을 어떻게 쉽게 만드는지에 대한 후기 글 을 썼습니다 . (실제로, 블록을 특정 종류의 모나드로 제한하려면 코드가 필요합니다. 재사용을 활성화하는 것이 기본값입니다.)


2
실용적인 것을 언급하는 유일한 대답 인 것에 대해 +1을 드릴 것입니다. 그러나 저는 IObservables프로덕션 코드에서 사용한 적이 없다고 생각 합니다.
JD

5
@JonHarrop 이것은 사실이 아닌 것 같습니다. F #에서 모든 이벤트는 IObservable이며, 책의 WinForms 장에있는 이벤트를 사용합니다.
Dax Fohl

1
마이크로 소프트는 저에게 그 책을 쓰기 위해 돈을 지불했고 그 기능을 다루도록 요구했습니다. 프로덕션 코드에서 이벤트를 사용한 것을 기억할 수 없지만 살펴 보겠습니다.
JD

너무 나는 가정 가능할 것이다 된 IQueryable 및 IEnumerable을 사이에 재사용
콜라

4 년 후 저는 조사를 마쳤습니다. 우리는 Rx를 생산에서 제거했습니다.
JD

13

Haskell에서 가장 많이 사용되는 유형 다형성의 예는 Monad인터페이스입니다. Functor그리고 Applicative내가 보여 줄게, 그래서 같은 방식으로 높은 kinded하는 Functor뭔가 간결을 보여주기 위해.

class Functor f where
    fmap :: (a -> b) -> f a -> f b

이제 유형 변수 f가 사용되는 방식을 살펴보면서 해당 정의를 검토합니다 . f가치가있는 유형을 의미 할 수 없음을 알 수 있습니다. 해당 유형 서명의 값은 함수에 대한 인수 및 결과이기 때문에 식별 할 수 있습니다. 따라서 유형 변수 ab값을 가질 수있는 유형입니다. 유형 표현식 f af b. 하지만 f그 자체는 아닙니다 . f더 높은 종류의 유형 변수의 예입니다. 주어진 *값을 가질 수 있습니다 유형의 종류, f종류가 있어야합니다 * -> *. 즉, 우리가 이전 조사에서 알 수 있기 때문에, 값을 가질 수있는 유형 소요입니다 ab값을해야합니다. 그리고 우리는 또한 알고 f af b 값이 있어야하므로 값이 있어야하는 유형을 반환합니다.

이것은 더 높은 종류의 유형 변수 f의 정의에 사용됩니다 Functor.

ApplicativeMonad인터페이스는 더 추가 할 수 있지만 그들은 호환입니다. 이것은 그들이 종류와 함께 유형 변수에서도 작동한다는 것을 의미합니다 * -> *.

더 높은 종류의 유형에 대해 작업하면 추가 수준의 추상화가 도입됩니다. 기본 유형에 대한 추상화를 만드는 데만 제한되지 않습니다. 다른 유형을 수정하는 유형에 대한 추상화를 만들 수도 있습니다.


4
더 높은 종류가 무엇인지에 대한 또 다른 훌륭한 기술적 설명은 그것이 무엇에 유용한 지 궁금하게 만듭니다. 실제 코드에서 이것을 어디에서 활용 했습니까?
JD
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.