Haskell : 타입 클래스 vs 함수 전달


16

나에게 항상 타입 클래스를 사용하는 대신 함수 인수를 전달할 수있는 것 같습니다. 예를 들어 평등 유형 클래스를 정의하는 대신

class Eq a where 
  (==)                  :: a -> a -> Bool

그리고 다른 함수에서 사용하여 형식 인수를 나타내는 인스턴스는 Eq다음 과 같아야합니다 .

elem                    :: (Eq a) => a -> [a] -> Bool

elem타입 클래스를 사용하지 않고 함수를 정의 하고 대신 작업을 수행하는 함수 인수를 전달할 수 없습니까?


2
이를 사전 통과라고합니다. 타입 클래스 제약 조건을 암시 적 인수로 생각할 수 있습니다.
Poscat

2
그렇게 할 수는 있지만 함수를 전달하지 않고 유형에 따라 "표준"기능을 사용하는 것이 훨씬 편리합니다.
Robin Zigmond

2
당신은 그것을 그렇게 넣을 수 있습니다. 그러나 나는 적어도 하나의 또 다른 중요한 장점이 있다고 주장한다. 특정 "인터페이스"또는 기능 세트를 구현하는 모든 유형에서 작동하는 다형성 함수를 작성하는 능력. 타입 클래스 제약 조건은 추가 함수 인수를 전달하지 않는 방식으로 매우 명확하게 표현한다고 생각합니다. 특히 많은 유형 클래스가 만족해야하는 (슬프게 암시적인) "법칙"때문에. Monad m제약은 유형의 부가 기능 인수를 전달하는 것보다 나에게 더 말한다 a -> m am a -> (a -> m b) -> m b.
Robin Zigmond


1
TypeApplications확장은 암시 적 인수가 명시 적으로 만들 수 있습니다. (==) @Int 3 5비교 35구체적 Int값. @Int유형별 Int비교 함수 자체가 아니라 유형별 동등 함수 사전에서 키로 생각할 수 있습니다 .
chepner

답변:


19

예. 이것을 "사전 통과 스타일"이라고합니다. 나는 약간 특히 까다로운 일을하고 경우에 사전 전달이 더 강력하기 때문에 때때로, I는 typeclass 스크랩 및 사전으로 바꿀 필요가 1 개념적으로 간단한 코드 모양은 매우 복잡하고, 또 종종 매우 성가신. 필자는 Haskell이 아닌 언어에서 사전 전달 스타일을 사용하여 유형 클래스를 시뮬레이트합니다.

물론 표현력에 차이가있을 때마다 절충이 발생합니다. 지정된 API를 DPS를 사용하여 작성된 경우 더 많은 방법으로 지정된 API를 사용할 수 있지만 API는 할 수없는 경우 자세한 정보를 얻습니다. 이것이 실제로 나타나는 한 가지 방법은입니다 Data.Set. 이는 Ord유형 당 하나의 사전 만 있다는 사실에 의존 합니다. Set매장의 요소에 따라 분류 Ord하면 하나 사전에 세트를 구축하고 다른 하나를 사용하여 요소를 삽입, DPS 가능한 것, 당신이 깰 수 있다면, 그리고 Set의 불변과 충돌이 발생합니다. 이 고유성 문제는 팬텀 존재를 사용하여 완화 할 수 있습니다API에 상당히 성가신 복잡성을 희생시키면서 사전을 표시하기 위해 입력합니다. 이것은 TypeableAPI 에서도 거의 같은 방식으로 나타납니다 .

고유성 비트는 자주 나타나지 않습니다. 타입 클래스에서 가장 좋은 것은 코드를 작성하는 것입니다. 예를 들어

catProcs :: (i -> Maybe String) -> (i -> Maybe String) -> (i -> Maybe String)
catProcs f g = f <> g

입력을 받아 출력을 제공 할 수있는 두 개의 "프로세서"가 필요하며이를 병합하여 병합하면 Nothing다음과 같은 DPS로 작성되어야합니다.

catProcs f g = (<>) (funcSemi (maybeSemi listSemi)) f g

우리는 타입 시그니처에서 이미 철자를 썼음에도 불구하고 본질적으로 다시 사용하는 타입을 철자해야했습니다. 심지어 컴파일러가 이미 모든 타입을 알고 있기 때문에 중복되었습니다. 주어진 Semigroup타입에서 주어진 것을 구성하는 유일한 방법이 있기 때문에 , 컴파일러는 당신을 위해 그것을 할 수 있습니다. 이것은 많은 매개 변수 인스턴스를 정의하고 Data.Functor.*결합 자에서와 같이 계산하기 위해 유형의 구조를 사용하기 시작할 때 "복합 관심"유형 효과를 가지며, 이는 deriving via본질적으로 모든 것을 얻을 수 있는 위치 에 큰 영향을 미칩니다 . 당신을 위해 쓰여진 당신의 타입의 "표준"대수 구조.

그리고 MPTC와 자금 조달을 시작하지 마십시오. 정보를 유형 검사 및 추론에 다시 제공합니다. 나는 그런 것을 DPS로 변환하려고 시도하지 않았습니다. 많은 유형 평등 증명을 통과해야한다고 생각합니다. 그러나 어쨌든 나는 그것이 편한 것보다 뇌에 훨씬 더 많은 일이 될 것이라고 확신합니다 와.

-

1 U 를 사용하지 않으면 reflection전원이 동등 해지지 만 reflection사용하기가 번거로울 수 있습니다.



저는 DPS를 통해 표현 된 자금에 매우 관심이 있습니다. 이 주제에 대한 재조정 가능한 자료를 알고 있습니까? 어쨌든 매우 이해하기 쉬운 설명입니다.

@bob, 맹렬한 것은 아니지만 흥미로운 탐험이 될 것입니다. 그것에 대해 새로운 질문을 하시겠습니까?
luqui

5

예. 그것은 (사전 전달이라고 함) 기본적으로 컴파일러가 어쨌든 타입 클래스를 작성하는 것입니다. 그 기능을 위해 문자 그대로 수행하면 다음과 같이 보일 것입니다.

elemBy :: (a -> a -> Bool) -> a -> [a] -> Bool
elemBy _ _ [] = False
elemBy eq x (y:ys) = eq x y || elemBy eq x ys

전화 elemBy (==) x xs는 이제와 같습니다 elem x xs. 그리고이 특정한 경우에, 당신은 한 걸음 더 나아갈 수 있습니다 : eq매번 같은 첫 번째 논증을가집니다, 그래서 당신은 그것을 적용하는 호출자의 책임으로 만들 수 있고, 이것으로 끝날 수 있습니다 :

elemBy2 :: (a -> Bool) -> [a] -> Bool
elemBy2 _ [] = False
elemBy2 eqx (y:ys) = eqx y || elemBy2 eqx ys

전화 elemBy2 (x ==) xs는 이제와 같습니다 elem x xs.

... 오 기다려 그저 any. (실제로 표준 라이브러리에서는elem = any . (==) .)


AFAIU 사전 전달은 유형 클래스 인코딩에 대한 스칼라의 접근 방식입니다. 이러한 추가 인수는로 선언 될 수 있으며 implicit컴파일러는 범위에서 인수를 삽입합니다.
michid
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.