Haskell에서 "Just"구문은 무엇을 의미합니까?


118

이 키워드가하는 일에 대한 실제 설명을 위해 인터넷을 샅샅이 뒤졌습니다. 내가 본 모든 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의미에 대한 좋은 설명은 대단히 감사하겠습니다.

답변:


211

실제로 모든 모듈로 자동으로 가져 오는 표준 라이브러리 인 Prelude 에서 정의되는 일반적인 데이터 생성자입니다 .

아마도 구조적으로

정의는 다음과 같습니다.

data Maybe a = Just a
             | Nothing

이 선언은 유형 Maybe a변수에 의해 매개 변수화되는 유형을 정의합니다. 즉 a, 대신 모든 유형과 함께 사용할 수 있습니다 a.

구성 및 파괴

형식에는 두 개의 생성자 Just aNothing. 형식에 여러 생성자가있는 경우 가능한 생성자 중 하나만 사용하여 형식 값이 생성되어야합니다. 이러한 유형의 값 중 하나를 통해 제조 하였다 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유용한 경우가 있습니다.

Functorfmap기본 유형 (예 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 Integerm_xInt -> Int함수 가있는 경우 실제로 값이 있는지 여부 에 대한 걱정없이에 직접 함수를 적용 f할 수 있습니다 . 실제로 해제 된 함수 의 전체 체인 을 값에 적용 할 수 있으며 완료했을 때 한 번만 명시 적으로 확인하는 것에 대해 걱정할 필요가 있습니다.fmap f m_xfMaybe IntegerInteger -> IntegerMaybe IntegerNothing

아마도 모나드로서

당신이 a의 개념에 얼마나 익숙한 지 잘 모르겠지만 Monad, 적어도 IO a전에 사용 해본 적이 있고 타입 시그니처 IO aMaybe a. 비록 IO그것이 당신에게 그것의 생성자를 노출하지 않습니다 따라서 만 하스켈 런타임 시스템에 의해 "실행"할 수 있다는 점에서 특별하다, 그것은 또한 아직 Functor인뿐만 아니라 Monad. 사실, a MonadFunctor몇 가지 추가 기능이 있는 특별한 종류 라는 중요한 의미가 있지만 여기에 들어가는 것은 아닙니다.

어쨌든, 모나드 IO는 "값을 초래하는 계산"을 나타내는 새로운 유형에 대한 맵 유형을 좋아 하고 , 정규 함수를 "평가를 통해 얻은 값을 생성하는 계산으로 바꾸는 Monad매우 fmap유사한 함수 를 통해 함수를 유형 으로 리프트 할 수 있습니다 liftM." 함수."

아마도 (지금까지 읽으 셨다면) Maybe그것도 Monad. "값을 반환하지 못할 수있는 계산"을 나타냅니다. fmap예제 와 마찬가지로 이를 통해 각 단계 후에 명시 적으로 오류를 확인하지 않고도 전체적인 계산을 수행 할 수 있습니다. 실제로 Monad인스턴스가 생성 되는 방식은 Maybe값에 대한 계산이 a 가 발생 하자마자 중지Nothing 되므로 계산 도중에 즉시 중단되거나 값이없는 반환과 같습니다.

당신은 아마 쓸 수 있었을 것입니다

앞서 말했듯 Maybe이 언어 구문이나 런타임 시스템에 구워진 유형 에는 고유 한 것이 없습니다 . Haskell이 기본적으로 제공하지 않았다면 모든 기능을 직접 제공 할 수 있습니다! 사실, 어쨌든 다른 이름으로 직접 다시 작성하고 동일한 기능을 얻을 수 있습니다.

Maybe이제 유형과 생성자 를 이해 하셨기를 바랍니다 .하지만 여전히 불분명 한 것이 있으면 알려주세요!


19
얼마나 뛰어난 대답입니까! Haskell은 종종 Maybe다른 언어가 사용 하는 곳에서 null또는 nil( NullPointerException모든 구석에 숨어있는 끔찍한)을 사용 한다는 점을 언급해야합니다 . 이제 다른 언어도이 구조를 사용하기 시작합니다. Scala as Option, 심지어 Java 8도 Optional유형 을 갖습니다 .
Landei 2013-09-15

3
이것은 훌륭한 설명입니다. 내가 읽은 많은 설명은 Just가 Maybe 유형에 대한 생성자라는 아이디어를 암시했지만 실제로는 그것을 명시 적으로 만들지 않았습니다.
reem

@ Landei, 제안 주셔서 감사합니다. 널 참조의 위험을 언급하기 위해 편집했습니다.
Levi Pearson

@Landei 옵션 유형은 70 년대 ML 이후로 사용되어 왔으며 Scala가 ML의 명명 규칙, 생성자와 함께 Option을 사용하기 때문에 Scala에서 선택했을 가능성이 높습니다.
stonemetal 2013-09-16

@Landei Apple의 Swift는 선택 사항도 꽤 많이 활용합니다
Jamin

37

현재 답변의 대부분은 Just친구들 이 어떻게 일 하는지에 대한 매우 기술적 인 설명입니다 . 나는 그것이 무엇을위한 것인지 설명하는 데 내 손을 뻗을 것이라고 생각했다.

많은 언어에는 null적어도 일부 유형의 경우 실제 값 대신 사용할 수있는 것과 같은 값 이 있습니다. 이것은 많은 사람들을 매우 화나게 만들고 나쁜 행동으로 널리 간주되었습니다. 그럼에도 불구 null하고 어떤 것이 없음을 나타내는 것과 같은 값을 갖는 것이 때때로 유용합니다 .

Haskell은 Nothing()를 가질 수있는 장소를 명시 적으로 표시함으로써이 문제를 해결합니다 null. 기본적으로 함수가 일반적으로 type을 반환하면 Foo대신 type을 반환해야합니다 Maybe Foo. 값이 없음을 나타내려면을 반환 Nothing합니다. 값을 반환 bar하려면 대신 반환해야합니다 Just bar.

따라서 기본적으로를 가질 Nothing수 없다면 Just. 당신이 가질 수 있다면 Nothing, 당신은 필요합니다 Just.

마법 같은 것은 없습니다 Maybe. Haskell 유형 시스템에 구축되었습니다. 즉, 일반적인 Haskell 패턴 매칭 트릭을 모두 사용할 수 있습니다 .


1
아주 좋은 다른 답변 옆에 대답,하지만 난 여전히 : 코드 예제 혜택을 누릴 것이라고 생각
PascalVKooten

13

유형을 감안할 때 t,의 값은 Just t유형의 기존 값 t, Nothing값에 도달 실패, 또는 값을 갖는 것은 의미가 될 경우를 나타냅니다.

귀하의 예에서 마이너스 잔액을 갖는 것은 의미가 없으므로 그러한 일이 발생하면 Nothing.

다른 예를 들면,이 소요 분할 함수를 정의 부문에서 사용될 수 ab, 복귀 Just a/b하는 경우는 b제로이고, Nothing그렇지. 예외에 대한 편리한 대안으로 또는 이전 예제와 같이 의미가없는 값을 대체하기 위해 이와 같이 자주 사용됩니다.


1
위의 코드에서 Just를 제거했는데 왜 작동하지 않습니까? Nothing을 원할 때마다 꼭 있어야합니까? 해당 표현식 내의 함수가 Nothing을 반환하는 표현식은 어떻게됩니까?
reem

7
을 제거하면 Just코드가 typecheck되지 않습니다. 그 이유 Just는 적절한 유형을 유지하기 위해서입니다. 형식 (실제로는 모나드이지만 형식이라고 생각하는 것이 더 쉽습니다) Maybe t이 있습니다 . 형식 Just t및 형식의 요소로 구성됩니다 Nothing. Nothinghas type 이므로 유형 값 Maybe t중 하나 Nothing또는 일부로 평가할 수있는 표현식이 t제대로 입력되지 않았습니다. 함수가 경우에 따라 반환 Nothing되는 경우 해당 함수를 사용하는 모든 표현식 isJust에는 가능한 모든 경우를 처리 할 수 ​​있도록 해당 함수 ( 또는 case 문) 를 확인하는 방법이 있어야합니다 .
qaphla 2013-09-15

2
그래서 Just는 Maybe 타입 내에서 일관성을 유지하기 위해 존재합니다. 왜냐하면 일반 t는 Maybe 타입이 아니기 때문입니다. 이제 모든 것이 훨씬 더 명확 해졌습니다. 감사합니다!
reem

3
@qaphla : "모나드, 사실, [...]"에 대한 귀하의 의견은 오해의 소지가 있습니다. Maybe t 입니다 단지 유형입니다. 에 대한 Monad인스턴스 가 있다는 사실 Maybe은 유형이 아닌 것으로 변환하지 않습니다.
Sarah

2

전체 함수 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합니다.


-1

기능 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

1
나는 확실히 당신은 정말 깊은 여기서 뭔가 말을 의미 해요
sehe

1
Haskell에는 유형 추론이 있습니다. 의 유형을 명시 할 필요가 없습니다 NothingJust newBalance명시 적으로는.
rightfold 2014 년

초보자에 대한이 설명을 위해 명시 적 유형은의 사용 의미를 명확히합니다 Just.
필립
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.