타입 클래스 yes-no

박준규·2023년 3월 17일

이 글은 책 Learn You a Haskell for Great Good!에서 섹션 A yes-no typeclass을 읽고 정리한 것이다.

타입 클래스 yes-no

자바스크립트 같은 약 타입(weakly typed) 언어에서는 if 표현식에 거의 아무거나 넣을 수 있다. 예를 들어 다음과 같은 표현식이 모두 가능하다.

if (0) alert("YEAH!") else alert("NO!")
if ("") alert("YEAH!") else alert("NO!")
if (false) alert("YEAH!") else alert("NO!")

위 표현식의 결과는 모두 alert("NO!")이다. 자바스크립트에서는 길이가 0이 아닌 문자열을 참처럼 취급하기 때문에 아래 표현식의 결과는 alert("YEAH!")가 된다.

if ("WHAT") alert("YEAH") else alert("NO!")

하스켈에서는 원래 위와 같이 if 표현식에 아무거나 넣을 수 없지만 재미로 한번 만들어 보자. 타입 클래스는 아래처럼 정의한다.

class YesNo a where
  yesno :: a -> Bool

타입 클래스 YesNo에 함수를 하나 정의하는데 이 함수는 타입이 a인 값을 하나 받아서 결과로 참인지 거짓인지를 알려 준다. 타입 a에는 구체적인 타입이 와야 한다.

이제 인스턴스를 정의해 보자. 정수 타입은 자바스크립트에서처럼 0이 아닌 값은 참이고 0은 거짓으로 취급한다.

instance YesNo Int where
  yesno 0 = False
  yesno _ = True

빈 리스트는 거짓이고 그렇지 않은 리스트는 참으로 본다.

instance YesNo [a] where
  yesno [] = False
  yesno _  = True

리스트 안에 어떤 타입이 들어 있는지는 상관이 없기 때문에 타입을 [a]로 적어도 된다. 타입 Bool은 딱히 판단할 게 없고 입력 그대로 리턴하면 된다.

instance YesNo Bool where
  yesno = id

함수 id는 입력한 값이 그대로 리턴되는 표준 라이브러리 함수이다.

타입 Maybe 인스턴스는 다음과 같다.

instance YesNo (Maybe a) where
  yesno (Just _) = True
  yesno Nothing  = False

Maybe 안에 어떤 값이 들어 있는지는 상관이 없기 때문에 특별히 타입 a를 제한할 필요는 없다. 그냥 생성자가 Just이면 참이고 Nothing이면 거짓이다. Maybe -> Bool 같은 타입은 있을 수 없기 때문에 Maybe라고만 적으면 안 되고 (Maybe a)라고 적어야 한다. 리스트와 마찬가지로 타입 Maybe 안에 어떤 타입이 오더라도 상관 없다.

앞에서 이진 탐색 트리를 표현하는 타입 Tree a를 정의했었는데 비어 있는 나무는 거짓으로 볼 수 있고 그렇지 않은 나무는 참으로 볼 수 있다.

instance YesNo (Tree a) where
  yesno EmptyTree = False
  yesno _         = True

아래처럼 신호등 타입도 인스턴스를 만들 수 있다.

instance YesNo TrafficLight where
  yesno Red = False
  yesno _   = True

다음과 같이 사용할 수 있다.

ghci> yesno $ length []  
False  
ghci> yesno "haha"  
True  
ghci> yesno ""  
False  
ghci> yesno $ Just 0  
True  
ghci> yesno True  
True  
ghci> yesno EmptyTree  
False  
ghci> yesno []  
False  
ghci> yesno [0,0,0]  
True  
ghci> :t yesno  
yesno :: (YesNo a) => a -> Bool

잘 된다! 이제 if 표현식과 비슷한데 YesNo로 동작하는 함수를 만들어 보자.

yesnoIf :: (YesNo y) => y -> a -> a -> a
yesnoIf yesnoVal yesResult noResult =
  if yesno yesnoVal
  then yesResult
  else noResult
ghci> yesnoIf [] "YEAH!" "NO!"  
"NO!"  
ghci> yesnoIf [2,3,4] "YEAH!" "NO!"  
"YEAH!"  
ghci> yesnoIf True "YEAH!" "NO!"  
"YEAH!"  
ghci> yesnoIf (Just 500) "YEAH!" "NO!"  
"YEAH!"  
ghci> yesnoIf Nothing "YEAH!" "NO!"  
"NO!"

참고

profile
코딩하는 물총새

0개의 댓글