여러 인수와 기능에 대한 일반적인 기술 - printf
같은 재귀 typeclass를 사용하는 --is. printf
의 경우 PrintfType
클래스로이 작업을 수행합니다.
(PrintfArg a, PrintfType r) => PrintfType (a -> r)
이 기본적으로 당신이 PrintfType
을 반환 할 경우, 함수는 또한 인스턴스 말한다 : 중요한 통찰력은 재귀 인스턴스입니다. "기본 케이스"는 String
과 같은 유형입니다. 따라서 printf
을 과 하나의 인수로 호출하려는 경우 PrintfType String
및 PrintfType (a -> r)
인 경우 이 String
인 두 개의 인스턴스가 발생합니다. 당신이 이 인수를 원하는 경우, 이동 : String
, (a -> r)
을 r
이 String
및 r
이전 (a -> r)
입니다 (a -> r)
입니다.
그러나 실제로 문제는 좀 더 복잡합니다. 2 개의 작업을 처리하는 인스턴스가 필요합니다. 인스턴스가 다른 유형의 함수 (예 : V (V a -> V b)
, V (V a -> V b -> V c)
등)에 적용되도록하고 적절한 수의 인수가 표시되도록합니다.
첫 번째 단계는 [String]
을 사용하여 인수를 전달하는 것입니다. 유형은 값의 개수에 대한 정보를 잃어 버리기 때문에 적절한 수의 인수가 있는지 확인할 수 없습니다. 대신, 인수의 개수를 반영하는 인수 목록에 형식을 사용해야합니다.
이 유형은 다음과 같이 보일 수 있습니다 : 그것은 두 개의 다른과 같이 사용할 수있는 유형, 결합하는 단지 형
data a :. b = a :. b
입니다 :
"foo" :. "bar" :: String :. String
"foo" :. "bar" :. "baz" :: String :. String :. String
지금 방금 작성해야 형식 수준의 인수 목록과 함수 자체를 탐색하는 재귀 적 인스턴스가있는 typecass입니다. 내 말은 매우 거친 독립형 스케치입니다. 당신은 당신 자신의 특별한 문제에 그것을 채택해야 할 것입니다.
infixr 8 :.
data a :. b = a :. b
class Callable f a b | f -> a b where
call :: V f -> a -> b
instance Callable rf ra (V rb) => Callable (String -> rf) (String :. ra) (V rb) where
call (V f) (a :. rest) = call (V (f a)) rest
instance Callable String() (V String) where
call (V f)() = V f
당신은 또한 몇 가지 확장 기능을 활성화해야합니다 : FlexibleInstances
, FucntionalDepenedencies
및 UndecidableInstances
을.
그런 다음이처럼 사용할 수 있습니다
당신이 잘못된 수의 인수에 전달하면
*Main> call (V "foo")()
V "foo"
*Main> call (V (\ x -> "foo " ++ x)) ("bar" :.())
V "foo bar"
*Main> call (V (\ x y -> "foo " ++ x ++ y)) ("bar" :. " baz" :.())
V "foo bar baz"
, 당신은 유형의 오류가 발생합니다. 틀림없이, 그것은 세상에서 가장 예쁜 오류 메시지가 아닙니다! 즉, 오류 (Couldn't match type `()' with `[Char] :.()'
) 의 중요한 부분은이 핵심 문제 (일치하지 않는 인수 목록)를 지적했기 때문에 따라야합니다. 이 문제에 대한 최고의 해결책 확신하지 했는데요 -이 특정 작업에-복잡 통해 비트가 될 수도
는
*Main> call (V (\ x -> "foo " ++ x)) ("bar" :. "baz" :.())
<interactive>:101:1:
Couldn't match type `()' with `[Char] :.()'
When using functional dependencies to combine
Callable String() (V String),
arising from the dependency `f -> a b'
in the instance declaration at /home/tikhon/Documents/so/call.hs:16:14
Callable [Char] ([Char] :.()) (V [Char]),
arising from a use of `call' at <interactive>:101:1-4
In the expression:
call (V (\ x -> "foo " ++ x)) ("bar" :. "baz" :.())
In an equation for `it':
it = call (V (\ x -> "foo " ++ x)) ("bar" :. "baz" :.())
참고. 그러나 좀 더 진보 된 typeclass 기능을 사용하여보다 복잡한 유형 수준의 불변성을 적용하는 것은 아주 좋은 연습입니다.
'V a -> V b -> V c'는 실제로 'V a -> (V b -> V c)'입니다. 유도를 통해이를 수행 할 수 있습니다. 기본 경우 :'V a'는 호출 가능합니다. 귀납적 인 경우 :'V a'가 호출 가능하다면,'V b -> V a'가 호출 가능합니다. 귀납적 인 경우의 인스턴스 헤드는'instance Callable k => Callable (m -> k) where'의 일반적인 형식을가집니다. –