이 키워드가하는 일에 대한 실제 설명을 위해 인터넷을 샅샅이 뒤졌습니다. 내가 본 모든 Haskell 튜토리얼은 무작위로 사용하기 시작하고 그것이 무엇을하는지 설명하지 않습니다 (그리고 많은 것을 보았습니다).
다음 은를 사용하는 Real World Haskell 의 기본 코드입니다 Just
. 코드가 무엇을하는지 이해하지만의 목적이나 기능이 무엇인지 이해하지 못합니다 Just
.
lend amount balance = let reserve = 100
newBalance = balance - amount
in if balance < reserve
then Nothing
else Just newBalance
내가 관찰 한 바에 Maybe
따르면 타이핑 과 관련이 있지만 그게 내가 배운 전부입니다.
Just
의미에 대한 좋은 설명은 대단히 감사하겠습니다.
답변
실제로 모든 모듈로 자동으로 가져 오는 표준 라이브러리 인 Prelude 에서 정의되는 일반적인 데이터 생성자입니다 .
아마도 구조적으로
정의는 다음과 같습니다.
data Maybe a = Just a
| Nothing
이 선언은 유형 Maybe a
변수에 의해 매개 변수화되는 유형을 정의합니다. 즉 a
, 대신 모든 유형과 함께 사용할 수 있습니다 a
.
구성 및 파괴
형식에는 두 개의 생성자 Just a
와 Nothing
. 형식에 여러 생성자가있는 경우 가능한 생성자 중 하나만 사용하여 형식 값이 생성되어야합니다. 이러한 유형의 값 중 하나를 통해 제조 하였다 Just
또는 Nothing
다른 (비 에러) 가능성은 없다.
Nothing
매개 변수 유형이 없기 때문에 생성자로 사용될 때 Maybe a
모든 유형 의 유형 멤버 인 상수 값의 이름을 지정합니다 a
. 그러나 Just
생성자에는 유형 매개 변수가 있습니다. 즉, 생성자로 사용될 때 유형에서으로의 함수처럼 작동 a
합니다 Maybe a
. 즉, 유형이a -> Maybe a
따라서 유형의 생성자는 해당 유형의 값을 빌드합니다. 다른 측면은 그 값을 사용하고 싶을 때이며 패턴 매칭이 작동하는 곳입니다. 함수와 달리 생성자는 패턴 바인딩 표현식에서 사용할 수 있으며, 이는 둘 이상의 생성자가있는 유형에 속하는 값의 케이스 분석 을 수행 할 수있는 방법입니다 .
Maybe a
패턴 일치에서 값 을 사용하려면 다음 과 같이 각 생성자에 대한 패턴을 제공해야합니다.
case maybeVal of
Nothing -> "There is nothing!"
Just val -> "There is a value, and it is " ++ (show val)
이 경우 식에서 첫 번째 패턴은 값이 Nothing
이면 일치하고 두 번째 패턴은 값이로 생성 된 경우 일치합니다 Just
. 두 번째 항목 이 일치하면 일치하는 값이 생성 될 때 생성자 val
에 전달 된 매개 변수에 이름 을 바인딩합니다 Just
.
의미하는 것
아마도 이것이 어떻게 작동하는지 이미 잘 알고 계실 것입니다. Maybe
값에 대한 마법은 없습니다. 그냥 일반적인 Haskell Algebraic Data Type (ADT) 일뿐입니다. 그러나 Integer
예에서 와 같이 유형 Nothing
을 가치 부족을 나타내는 추가 값 ( )이 있는 새로운 컨텍스트로 효과적으로 “리프트”하거나 확장하기 때문에 상당히 많이 사용됩니다 ! 유형의 시스템은 당신이 얻을 수 있도록하기 전에 당신이 여분의 값을 확인해야 Integer
하는 수도 가. 이것은 엄청난 수의 버그를 방지합니다.
오늘날 많은 언어가 NULL 참조를 통해 이러한 종류의 “값이없는”값을 처리합니다. 저명한 컴퓨터 과학자 인 Tony Hoare (그는 Quicksort를 발명했고 Turing Award 수상자)는 그의 “10 억 달러의 실수” 로이를 소유하고 있습니다 . Maybe 유형은이 문제를 해결할 수있는 유일한 방법은 아니지만 효과적인 방법으로 입증되었습니다.
아마도 Functor로서
이러한 기존의 유형에 대한 작업을 할 수있는 다른 하나 개의 유형을 변환의 개념 또한 새로운 유형의 작업으로 변환 할 수라는 하스켈 타입 클래스 뒤에 개념 Functor
, Maybe a
유용한 경우가 있습니다.
Functor
는 fmap
기본 유형 (예 Integer
:)의 값에 걸쳐있는 함수를 해제 된 유형 (예 :)의 값에 걸쳐있는 함수에 매핑 하는라는 메서드를 제공합니다 Maybe Integer
. 값 fmap
에 대해 작동하도록 변환 된 함수 Maybe
는 다음과 같이 작동합니다.
case maybeVal of
Nothing -> Nothing -- there is nothing, so just return Nothing
Just val -> Just (f val) -- there is a value, so apply the function to it
따라서 Maybe Integer
값 m_x
과 Int -> Int
함수 가있는 경우 실제로 값이 있는지 여부 에 대한 걱정없이에 직접 함수를 적용 f
할 수 있습니다 . 실제로 해제 된 함수 의 전체 체인 을 값에 적용 할 수 있으며 완료했을 때 한 번만 명시 적으로 확인하는 것에 대해 걱정할 필요가 있습니다.fmap f m_x
f
Maybe Integer
Integer -> Integer
Maybe Integer
Nothing
아마도 모나드로서
당신이 a의 개념에 얼마나 익숙한 지 잘 모르겠지만 Monad
, 적어도 IO a
전에 사용 해본 적이 있고 타입 시그니처 IO a
는 Maybe a
. 비록 IO
그것이 당신에게 그것의 생성자를 노출하지 않습니다 따라서 만 하스켈 런타임 시스템에 의해 “실행”할 수 있다는 점에서 특별하다, 그것은 또한 아직 Functor
인뿐만 아니라 Monad
. 사실, a Monad
가 Functor
몇 가지 추가 기능이 있는 특별한 종류 라는 중요한 의미가 있지만 여기에 들어가는 것은 아닙니다.
어쨌든, 모나드 IO
는 “값을 초래하는 계산”을 나타내는 새로운 유형에 대한 맵 유형을 좋아 하고 , 정규 함수를 “평가를 통해 얻은 값을 생성하는 계산으로 바꾸는 Monad
매우 fmap
유사한 함수 를 통해 함수를 유형 으로 리프트 할 수 있습니다 liftM
.” 함수.”
아마도 (지금까지 읽으 셨다면) Maybe
그것도 Monad
. “값을 반환하지 못할 수있는 계산”을 나타냅니다. fmap
예제 와 마찬가지로 이를 통해 각 단계 후에 명시 적으로 오류를 확인하지 않고도 전체적인 계산을 수행 할 수 있습니다. 실제로 Monad
인스턴스가 생성 되는 방식은 Maybe
값에 대한 계산이 a 가 발생 하자마자 중지Nothing
되므로 계산 도중에 즉시 중단되거나 값이없는 반환과 같습니다.
당신은 아마 쓸 수 있었을 것입니다
앞서 말했듯 Maybe
이 언어 구문이나 런타임 시스템에 구워진 유형 에는 고유 한 것이 없습니다 . Haskell이 기본적으로 제공하지 않았다면 모든 기능을 직접 제공 할 수 있습니다! 사실, 어쨌든 다른 이름으로 직접 다시 작성하고 동일한 기능을 얻을 수 있습니다.
Maybe
이제 유형과 생성자 를 이해 하셨기를 바랍니다 .하지만 여전히 불분명 한 것이 있으면 알려주세요!
답변
현재 답변의 대부분은 Just
친구들 이 어떻게 일 하는지에 대한 매우 기술적 인 설명입니다 . 나는 그것이 무엇을위한 것인지 설명하는 데 내 손을 뻗을 것이라고 생각했다.
많은 언어에는 null
적어도 일부 유형의 경우 실제 값 대신 사용할 수있는 것과 같은 값 이 있습니다. 이것은 많은 사람들을 매우 화나게 만들고 나쁜 행동으로 널리 간주되었습니다. 그럼에도 불구 null
하고 어떤 것이 없음을 나타내는 것과 같은 값을 갖는 것이 때때로 유용합니다 .
Haskell은 Nothing
()를 가질 수있는 장소를 명시 적으로 표시함으로써이 문제를 해결합니다 null
. 기본적으로 함수가 일반적으로 type을 반환하면 Foo
대신 type을 반환해야합니다 Maybe Foo
. 값이 없음을 나타내려면을 반환 Nothing
합니다. 값을 반환 bar
하려면 대신 반환해야합니다 Just bar
.
따라서 기본적으로를 가질 Nothing
수 없다면 Just
. 당신이 가질 수 있다면 Nothing
, 당신은 필요합니다 Just
.
마법 같은 것은 없습니다 Maybe
. Haskell 유형 시스템에 구축되었습니다. 즉, 일반적인 Haskell 패턴 매칭 트릭을 모두 사용할 수 있습니다 .
답변
유형을 감안할 때 t
,의 값은 Just t
유형의 기존 값 t
, Nothing
값에 도달 실패, 또는 값을 갖는 것은 의미가 될 경우를 나타냅니다.
귀하의 예에서 마이너스 잔액을 갖는 것은 의미가 없으므로 그러한 일이 발생하면 Nothing
.
다른 예를 들면,이 소요 분할 함수를 정의 부문에서 사용될 수 a
와 b
, 복귀 Just a/b
하는 경우는 b
제로이고, Nothing
그렇지. 예외에 대한 편리한 대안으로 또는 이전 예제와 같이 의미가없는 값을 대체하기 위해 이와 같이 자주 사용됩니다.
답변
전체 함수 a-> b는 유형 a의 가능한 모든 값에 대해 유형 b의 값을 찾을 수 있습니다.
Haskell에서 모든 기능이 총체적인 것은 아닙니다. 이 특별한 경우 함수 lend
는 총액이 아닙니다-잔액이 적립금보다 적은 경우에 대해 정의되지 않았습니다 (내 취향에 따라 newBalance가 적립금보다 적도록 허용하지 않는 것이 더 합리적입니다-그대로 101을 빌릴 수 있습니다 잔액 100).
비 합계 기능을 다루는 기타 설계 :
- 입력 값이 범위에 맞지 않는지 확인하면 예외 발생
- 특수 값 반환 (기본 유형) : 선호하는 선택은 자연수를 반환하는 정수 함수에 대한 음수 값입니다 (예 : String.indexOf-하위 문자열을 찾을 수없는 경우 반환 된 색인은 일반적으로 음수로 설계됨).
- 특수 값 (포인터) 반환 : NULL 또는 일부
- 아무것도하지 않고 조용히 돌아옵니다.
lend
대출 조건이 충족되지 않은 경우 이전 잔액을 반환하도록 작성할 수 있습니다. - 특수 값 반환 : Nothing (또는 일부 오류 설명 개체를 래핑하는 Left)
이는 전체 기능을 강제 할 수없는 언어에서 필요한 설계 제한 사항입니다 (예 : Agda는 할 수 있지만 튜링 불완전한 것과 같은 다른 복잡성을 유발 함).
특별한 값을 반환하거나 예외를 던질 때의 문제는 호출자가 실수로 그러한 가능성을 처리하는 것을 생략하기 쉽다는 것입니다.
실패를 조용히 버리는 문제도 분명합니다. 호출자가 함수로 할 수있는 일을 제한하고 있습니다. 예를 들어, lend
이전 잔액 이 반환 된 경우 호출자는 잔액이 변경되었는지 알 수 없습니다. 의도 된 목적에 따라 문제가 될 수도 있고 아닐 수도 있습니다.
Haskell의 솔루션은 부분 함수의 호출자가와 같은 유형을 처리 Maybe a
하거나 Either error a
함수의 반환 유형으로 인해 강제로 처리 합니다.
이 방법 lend
은 정의 된대로 항상 새 잔액을 계산하지 않는 함수입니다. 일부 상황에서는 새 잔액이 정의되지 않습니다. 특수 값 Nothing을 반환하거나 Just에서 새 잔액을 래핑하여 호출자에게이 상황을 알립니다. 이제 발신자는 자유롭게 선택할 수 있습니다. 특별한 방법으로 대출 실패를 처리하거나 오래된 잔액을 무시하고 사용 maybe oldBalance id $ lend amount oldBalance
합니다.
답변
기능 if (cond :: Bool) then (ifTrue :: a) else (ifFalse :: a)
의 동일한 유형이 있어야 ifTrue
하고 ifFalse
.
따라서를 작성할 때 type in을 then Nothing
사용해야 Maybe a
합니다.else f
if balance < reserve
then (Nothing :: Maybe nb) -- same type
else (Just newBalance :: Maybe nb) -- same type