하스켈이하는 방법은 다음과 같습니다.
경고 : 심각한 Haskell 팬보이의 긴 답변.
TL; DR
이 예제는 Haskell과 C #의 차이점을 정확하게 보여줍니다. 구조 생성의 물류를 생성자에게 위임하는 대신 주변 코드에서 처리해야합니다. 널 (Null Nothing
) 값 이 널 (Null )이 아닌 값을 기대하는 위치에서 널 ( Oskell의 값) 값을 자를 수있는 방법은 없습니다. 널 ( Null) 값은 일반 랩퍼 유형 Maybe
과 교환 가능하거나 직접 변환 할 수없는 특수 랩퍼 유형 내에서만 발생할 수 있기 때문입니다. 널 입력 가능 유형. 로 래핑하여 nullable 값을 사용 Maybe
하려면 먼저 패턴 일치를 사용하여 값을 추출해야합니다.이를 통해 제어 흐름을 null이 아닌 값이 있음을 확실히 알 수있는 분기로 제어 흐름을 전환해야합니다.
따라서:
nullable이 아닌 참조가 어떤 상황에서도 유효하지 않다는 것을 항상 알 수 있습니까?
예. Int
그리고 Maybe Int
두 개의 완전히 분리 된 종류가 있습니다. Nothing
평야에서 찾는 것은 에서 Int
"fish"문자열을 찾는 것과 비교할 수 있습니다 Int32
.
null이 아닌 참조 유형의 필드를 가진 객체의 생성자에서는 어떻습니까?
문제 없음 : Haskell의 값 생성자는 아무 것도 할 수 없으며 주어진 값을 가져와 합칩니다. 모든 초기화 로직은 생성자가 호출되기 전에 발생합니다.
참조를 작성 해야하는 코드에서 예외가 발생하여 객체가 완성되는 해당 객체의 마무리 장치는 어떻습니까?
Haskell에는 종료자가 없으므로 실제로이 문제를 해결할 수 없습니다. 그러나 나의 첫 반응은 여전히 유효합니다.
전체 답변 :
Haskell에는 null이 없으며 Maybe
데이터 형식을 사용하여 nullable을 나타냅니다. 어쩌면 다음과 같이 정의 된 조류 데이터 유형 일 수 있습니다 .
data Maybe a = Just a | Nothing
Haskell에 익숙하지 않은 사용자는 "A Maybe
는 a Nothing
또는 Just a
"입니다. 구체적으로 :
Maybe
는 IS 타입 생성자 :이 (제네릭 클래스로 (잘못) 생각 될 수있는 a
유형 변수입니다). C # 비유는 class Maybe<a>{}
입니다.
Just
A는 값 생성자 :이 유형의 하나 개의 인수를 취하는 함수 a
와 타입의 값 반환 Maybe a
값이 포함되어 있습니다. 따라서 코드 x = Just 17
는와 유사합니다 int? x = 17;
.
Nothing
또 다른 값 생성자이지만 인수 Maybe
를 사용하지 않으며 반환 된 "Nothing"이외의 값이 없습니다. (우리 가 Haskell에서 우리를 제한한다고 가정하면 을 쓸 수 있다고 가정 x = Nothing
)과 유사합니다 .int? x = null;
a
Int
x = Nothing :: Maybe Int
이제 Maybe
유형 의 기본 사항 이 제대로 작성되지 않았으므로 Haskell은 OP의 질문에서 논의 된 문제를 어떻게 피합니까?
음, 하스켈은 정말 지금까지 논의 된 언어의 가장 다른, 그래서 나는 몇 가지 기본적인 언어 원리를 설명하는 것으로 시작하겠습니다.
우선 Haskell에서는 모든 것이 불변 입니다. 모두. 이름은 값을 저장할 수있는 메모리 위치가 아니라 값을 나타냅니다 (이것만으로도 엄청난 버그 제거 원인이됩니다). 변수 선언 대입 그들의 값을 정의하여 만든 하스켈 값의 두 개의 별도의 연산이다 C #을 달리 (예를 들어 x = 15
, y = "quux"
, z = Nothing
), 변경할 수 없다한다. 따라서 코드는 다음과 같습니다.
ReferenceType x;
하스켈에서는 불가능합니다. 존재 null
하기 위해서는 모든 것을 명시 적으로 값으로 초기화해야 하기 때문에 값을 초기화하는 데 아무런 문제가 없습니다 .
둘째, Haskell은 객체 지향 언어가 아닙니다 . 순전히 기능적인 언어이므로 엄격한 의미의 객체는 없습니다. 대신, 인수를 취하고 합쳐진 구조를 반환하는 함수 (값 생성자)가 있습니다.
다음 으로 명령형 스타일 코드는 전혀 없습니다. 이것은 대부분의 언어가 다음과 같은 패턴을 따른다는 것을 의미합니다.
do thing 1
add thing 2 to thing 3
do thing 4
if thing 5:
do thing 6
return thing 7
프로그램 동작은 일련의 명령어로 표현됩니다. 객체 지향 언어에서 클래스 및 함수 선언도 프로그램 흐름에서 큰 역할을하지만 본질적으로 프로그램 실행의 "고기"는 일련의 명령 형태로 실행됩니다.
하스켈에서는 불가능합니다. 대신, 프로그램 흐름은 전적으로 체인 기능에 의해 결정됩니다. 명령형으로 보이는 do
표기법 조차도 익명의 기능을 >>=
연산자 에게 전달하기위한 구문 설탕 일뿐입니다 . 모든 기능은 다음과 같은 형태를 취합니다.
<optional explicit type signature>
functionName arg1 arg2 ... argn = body-expression
body-expression
값으로 평가되는 것은 어디에나 있을 수 있습니다. 분명히 더 많은 구문 기능을 사용할 수 있지만 요점은 문장 시퀀스가 완전히 없다는 것입니다.
마지막으로, 아마도 가장 중요한 것은 Haskell의 타입 시스템은 엄청나게 엄격 하다는 것입니다 . Haskell 타입 시스템의 중심 디자인 철학을 요약해야한다면, "컴파일 타임에 가능한 많은 것들이 런타임에 가능한 한 잘못되도록합니다."라고 말할 것입니다. 어떠한 암시 적 변환 (AN 홍보하려는이 없습니다 Int
A와를 Double
? 사용 fromIntegral
기능). 런타임에 유효하지 않은 값을 가질 수있는 유일한 방법은 사용 Prelude.undefined
하는 것입니다 (겉보기 만 있으면 제거 할 수 없음 ).
이 모든 것을 염두에두고 amon의 "손상된"예 를보고 Haskell에서이 코드를 다시 표현해 봅시다 . 먼저 데이터 선언 (명명 된 필드에 레코드 구문 사용) :
data NotSoBroken = NotSoBroken {foo :: Foo, bar :: Bar }
( foo
그리고 bar
정말 익명 필드 여기 대신 실제 필드에 액세서 기능을하고 있지만 우리는이 세부 사항을 무시할 수 있습니다).
NotSoBroken
값 생성자는 복용 이외의 조치를 취할 수없는 것입니다 Foo
과 Bar
(null 허용되지 않는)를하고 만드는 NotSoBroken
그들 중입니다. 명령형 코드를 넣거나 필드를 수동으로 할당 할 장소가 없습니다. 모든 초기화 로직은 다른 곳에서 이루어져야하며, 대부분 전용 팩토리 기능이어야합니다.
이 예에서 구성은 Broken
항상 실패합니다. NotSoBroken
값 생성자를 비슷한 방식으로 깰 수있는 방법은 없지만 (코드를 작성할 곳은 없습니다) 유사하게 결함이있는 팩토리 함수를 만들 수 있습니다.
makeNotSoBroken :: Foo -> Bar -> Maybe NotSoBroken
makeNotSoBroken foo bar = Nothing
(첫 번째 줄은 형식 서명 선언입니다. makeNotSoBroken
a Foo
와 a Bar
를 인수로 사용하고을 생성합니다 Maybe NotSoBroken
).
반환 유형은 Maybe NotSoBroken
단순히 에 대한 값 생성자 인 NotSoBroken
평가하도록 지시했기 때문에가 아니라 반드시 있어야합니다 . 우리가 다른 것을 썼다면 타입은 단순히 정렬되지 않을 것입니다.Nothing
Maybe
절대적으로 무의미한 것 외에도이 기능은 사용하려고 할 때 볼 수 있듯이 실제 목적을 달성하지 못합니다. useNotSoBroken
a NotSoBroken
를 인수로 기대 하는 함수를 작성해 봅시다 :
useNotSoBroken :: NotSoBroken -> Whatever
(를 인수로 useNotSoBroken
받아들이고를 NotSoBroken
생성합니다 Whatever
).
그리고 그렇게 사용하십시오 :
useNotSoBroken (makeNotSoBroken)
대부분의 언어에서 이러한 종류의 동작으로 인해 널 포인터 예외가 발생할 수 있습니다. Haskell에서 형식이 일치하지 않습니다 :를 makeNotSoBroken
반환 Maybe NotSoBroken
하지만 useNotSoBroken
a를 기대합니다 NotSoBroken
. 이러한 유형은 호환되지 않으며 코드 컴파일에 실패합니다.
이 문제를 해결하기 위해, 패턴 일치 라는 기능을 사용하여 값 case
의 구조를 기반으로 분기 하는 명령문을 사용할 수 있습니다 .Maybe
case makeNotSoBroken of
Nothing -> --handle situation here
(Just x) -> useNotSoBroken x
분명히이 스 니펫은 실제로 컴파일하기 위해 일부 컨텍스트 안에 배치해야하지만 Haskell이 nullable을 처리하는 방법의 기본 사항을 보여줍니다. 위 코드에 대한 단계별 설명은 다음과 같습니다.
- 먼저
makeNotSoBroken
type의 값을 생성하도록 보장됩니다 Maybe NotSoBroken
.
case
문은이 값의 구조를 검사합니다.
- 값이
Nothing
인 경우 "여기 상황 처리"코드가 평가됩니다.
- 값이 대신 값과 일치
Just
하면 다른 분기가 실행됩니다. 일치하는 절이 동시에 값을 Just
구성 으로 식별 하고 내부 NotSoBroken
필드를 이름 (이 경우 x
)에 바인딩 하는 방법에 유의하십시오 . x
그런 다음 정상 NotSoBroken
값 처럼 사용할 수 있습니다 .
따라서 패턴 일치는 개체의 구조가 제어 분기와 불가분의 관계가 있기 때문에 형식 안전성을 강화하는 강력한 기능을 제공합니다 .
나는 이것이 이해하기 쉬운 설명이기를 바란다. 이해가되지 않으면 Learn You A Haskell For Great Good! 으로 넘어갑니다 . , 내가 읽은 최고의 온라인 언어 자습서 중 하나입니다. 이 언어에서 내가하는 것과 같은 아름다움을 볼 수 있기를 바랍니다.