소켓 통신 코드를 구현하면서 Either에 담을 결과값으로 Unit을 넣었다. Unit 타입을 사용한 이유는 리턴값은 없지만 성공적으로 함수를 실행했다는 정보를 받고 싶었기 때문이다. 그렇다면 void를 쓸 수도 있을텐데 왜 Unit을 써야할까?
Unit 타입이란?
unit이라는 네이밍은 "단 하나의 값"만을 가진다는 특성에서 유래되었다. unit타입의 모든 값은 동일하며 그 자체로 어떤 정보를 표현하지는 않는다.
카테고리 이론에서 unit 타입은 종단객체(terminal object)이다. 종단객체는 모든 객체에서 단 하나의 방향으로 가는 화살표, 보편적인 끝을 찾고싶을 때의 사상(morphism)을 표현하기 위해 사용한다. 이를 프로그래밍에서는 "결과값이 필요없는 함수의 반환타입"으로 활용하는 것이다.
이처럼 Unit은 종단 객체(Terminal Object)이므로, 모든 객체 X에서 Unit으로 가는 유일한 사상을 가진다. 따라서 Unit은 본질적으로 단 하나의 값만 존재하며, 대부분의 언어에서는 이를 싱글톤 객체로 구현한다.
fpdart 코드를 들여다보면 위와 같이 싱글톤 타입으로 선언되었음을 확인할 수 있다.
Dart 언어에서의 void와 unit
(Haskell 같은) 함수형 언어에서 void는 아예 존재할 수 없다는 뜻으로 쓰이지만 Dart에서 void는 null을 반환할 수 있는 타입으로 동작한다.
Haskell 에서 print(hello)를 실행하는 코드를 작성하면 위와같이 에러가 발생하는 것을 알 수 있다. Haskell 같은 함수형 언어에서는 void타입이 절대 값이 존재할 수 없는 타입이기 때문에 단순 print(Hello)를 찍더라도 어쨋든 무언가를 반환하고 있어서 타입 불일치 오류가 발생하는것이다.
반면 Dart 같은 경우 같은 코드임에도 에러가 나지 않고있음을 확인할 수 있는데 이는 내부적으로 void가 null을 반환할 수 있는 타입으로 동작하기 때문이다.
그래서 왜 void 대신 unit?
Dart에서 void는 null을 반환할 수 있는 타입이므로, 타입 안정성을 완전히 보장하지 않는다. null은 여러 타입(String?, Int?, Double?)에 할당될 수 있어, 이를 사용할 경우 ! 연산자로 강제 언래핑하거나 ?? 연산자로 값을 제공해야 하는 불편함이 있다 . 마치 any타입으로 선언한 변수를 타입캐스팅해주는 것과 비슷해져버리는 것이다. 반면 Unit은 null과 달리 실제 값을 가지며, 오직 Unit 타입으로만 사용할 수 있다. 따라서 Unit을 사용하면 타입 안정성을 유지할 수 있다
왜 함수형프로그래밍에서 TypeSafety가 중요할까? 바로 참조투명성 때문이다. Nullable 타입을 사용하면 실행 흐름이 복잡해지고, null이 포함될 경우 추가적인 예외 처리가 필요해진다. 반면, Unit을 사용하면 '값이 있지만 의미 없는 값'이라는 의도를 표현하면서 타입 안정성을 유지할 수 있다.
총 정리하자면
Unit: 함수형 프로그래밍에서 "값이 필요 없지만, 실행이 성공했음을 나타내고 싶을 때" 사용한다.
Void: "절대 발생하지 않는 값(불가능한 상태)"를 표현할 때 사용된다.
'Functional Programming' 카테고리의 다른 글
fold 함수와 reduce 함수의 차이 (0) | 2025.02.08 |
---|---|
Future 와 Task의 차이 (0) | 2025.01.19 |