누군가 Haskell Prelude가 지수화를 위해 두 개의 개별 함수를 정의하는 이유를 말해 줄 수 있습니까 (예 : ^
and **
)? 나는 타입 시스템이 이런 종류의 중복을 제거해야한다고 생각했습니다.
Prelude> 2^2
4
Prelude> 4**0.5
2.0
답변:
실제로 세 가지 지수 연산자가 있습니다 : (^)
, (^^)
및 (**)
. ^
음이 아닌 적분 지수, ^^
정수 지수 및 **
부동 소수점 지수입니다.
(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a
그 이유는 유형 안전성 때문입니다. 수치 연산의 결과는 일반적으로 입력 인수와 동일한 유형을 갖습니다. 그러나를 Int
부동 소수점 거듭 제곱으로 올리고 유형 결과를 얻을 수는 없습니다 Int
. 따라서 유형 시스템은이 작업을 방지합니다 (1::Int) ** 0.5
. 유형 오류가 발생합니다. 동일은 간다 (1::Int) ^^ (-1)
.
이것을 넣는 또 다른 방법 : Num
유형은 아래에 닫히고 ^
(승법 역수를 가질 필요는 없음), Fractional
유형은 아래에 닫히고 ^^
, Floating
유형은 아래에 닫힙니다 **
. 에 대한 Fractional
인스턴스 가 없기 때문에 Int
음의 거듭 제곱으로 올릴 수 없습니다.
이상적으로의 두 번째 인수는 ^
음이 아니도록 정적으로 제한됩니다 (현재 1 ^ (-2)
런타임 예외 발생). 그러나 Prelude
.
Haskell의 유형 시스템은 세 가지 지수 연산자를 하나로 표현할만큼 강력하지 않습니다. 정말로 원하는 것은 다음과 같습니다.
class Exp a b where (^) :: a -> b -> a
instance (Num a, Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a, Floating b) => Exp a b where ... -- current **
다중 매개 변수 유형 클래스 확장을 켜도 실제로 작동하지 않습니다. 인스턴스 선택이 현재 Haskell에서 허용하는 것보다 더 영리해야하기 때문입니다.
Int
및 Integer
. 이 세 가지 인스턴스 선언을 가질 수 있으려면 인스턴스 확인에서 역 추적을 사용해야하며 Haskell 컴파일러는이를 구현하지 않습니다.
두 개의 연산자를 정의하지 않고 세 개를 정의합니다! 보고서에서 :
세 가지 두 인수 지수 연산이 있습니다. (
^
) 임의의 수를 음이 아닌 정수 거듭 제곱으로, (^^
) 분수를 임의의 정수 거듭 제곱으로, (**
) 두 개의 부동 소수점 인수를 취합니다.x^0
또는 의 값은 0을 포함하여x^^0
모든x
에 대해 1입니다 .0**y
정의되지 않았습니다.
즉, 세 가지 다른 알고리즘이 있으며 그 중 두 개는 정확한 결과 ( ^
및 ^^
)를 **
제공하고 대략적인 결과 를 제공합니다. 사용할 연산자를 선택하여 호출 할 알고리즘을 선택합니다.
^
두 번째 인수가 Integral
. 내가 착각하지 않았다면 적분 지수로 작업하는 것을 안다면 구현이 더 효율적일 수 있습니다. 또한 다음과 같은 것을 원한다면2 ^ (1.234)
의 밑이 적분, 2 인 경우에도, 당신의 결과는 분명히 분수 일 것입니다. 지수 함수에 들어가고 나가는 유형을보다 엄격하게 제어 할 수 있도록 더 많은 옵션이 있습니다.
Haskell의 유형 시스템은 C, Python 또는 Lisp와 같은 다른 유형 시스템과 동일한 목표를 가지고 있지 않습니다. 덕 타이핑은 (거의) 하스켈 사고 방식과 반대입니다.
class Duck a where quack :: a -> Quack
오리에 대해 우리가 기대하는 것을 정의하고 각 인스턴스는 오리처럼 행동 할 수있는 것을 지정합니다.
Duck
.