패턴 매칭은 표현력 있는 분기 코드를 가능하게 하고, 구조 분해는 값과 변수를 우아하게 묶어줘요.
패턴 매칭은 case문을 사용해 값을 변수에 묶거나, 와일드카드 _로 값을 버릴 수 있어요.
type Entity = Alien | Stranger (Maybe String) | Friend { name: String }
hello : Entity -> String
hello entity =
case entity of
-- Custom type variant
Alien -> "Hello, you are not from around here are you?"
-- Binding to a litteral value nested in Stranger
Stranger (Just nametag) -> "Hello, erm... " ++ nametag ++ "."
-- Discarding Friend's data
Friend _ -> "Hi!"
-- All other cases
_ -> "Hello stranger!"
구조 분해는 데이터에 가능한 형태가 하나뿐일 때마다 let 바인딩, 함수 인자, 그리고 물론 case 식에서 값을 묶을 수 있어요.
pairSum : ( Int, Int ) -> Int
pairSum pair =
-- Destructuring of a pair in a 'let' binding
let ( x, y ) = pair
in x + y
-- Destructuring in the function argument
pairSum : ( Int, Int ) -> Int
pairSum ( x, y ) = x + y
-- Custom type containing a single variant
type Container = Box String
-- Destructuring in the function argument
unbox : Container -> String
unbox (Box str) = str
-- Destructuring combined with pattern matching
unboxMaybe : Maybe Container -> Maybe String
unboxMaybe maybeContainer =
case maybeContainer of
Nothing -> Nothing
Just (Box "42") -> Just "The answer to the universe!"
Just (Box str) -> Just str
구조 분해는 레코드에도 사용할 수 있고, as 키워드와 함께 쓸 수도 있어요.
type alias Circle =
{ radius : Float
, center : ( Float, Float )
}
perimeter : Circle -> Float
perimeter { radius } =
2 * pi * radius
left : Circle -> Float
left { radius, center } =
-- the pair center cannot be pattern matched directly in the function argument
-- so we do it in a 'let' statement
let ( x, _ ) = center
in x - radius
-- using the 'as' keyword to bind both the fields and the whole record
smaller : Float -> Circle -> Circle
smaller reduction ({ radius } as circle) =
if reduction < radius then
{ circle | radius = radius - reduction }
else
circle
IT 헬프데스크에서 일하고 있고, 티켓 시스템을 더 효율적으로 개선해서 관리자에게 통계를 제공하고 싶어요.
티켓에는 상태(new, in progress...)와 작성자(사용자 이름과 직원 ID로 식별돼요)가 있고, IT 직원에게 할당될 수 있으며, 댓글 기록이 담겨 있어요.
지금의 티켓 시스템을 개선할 네 가지 작업이 있어요.
빈 댓글은 인터페이스를 어지럽히고 아무 정보도 주지 않으니, 이런 댓글을 걸러내고 싶어요.
사용자/댓글 쌍을 받아서 빈 댓글이면 True를 반환하는 헬퍼 함수 emptyComment를 정의해요.
함수 인자에서 바로 구조 분해를 사용해 사용자와 댓글을 추출해요. 사용자 정보는 필요 없으니 무시해요.
emptyComment ( User "Alice", "" )
-- => True
관리자는 티켓 처리 과정에 사용자들이 얼마나 참여하는지에 대한 통계를 원해요. 관리자는 티켓 작성자가 댓글을 몇 개나 남겼는지 확인해 달라고 해요.
티켓을 받아서 티켓 작성자가 추가한 댓글 수를 반환하는 numberOfCreatorComments를 정의해요.
함수 인자에서 구조 분해를 사용해 Ticket 타입에서 레코드를 추출하면서 동시에 레코드에서 필요한 필드도 추출해요.
그다음 let 바인딩에서 createdBy를 구조 분해해서 관련 댓글을 셀 수 있게 해요.
List 함수로 개수를 세고, 익명 함수의 인자에서 구조 분해를 사용해 사용자/댓글 쌍을 분리해요.
numberOfCreatorComments
numberOfCreatorComments
(Ticket
{ status = Closed
, createdBy = ( User "Chiaki", 1 )
, assignedTo = Nothing
, comments = [ ( User "Chiaki", "Never mind, fixed it." ) ]
}
)
-- => 1
일부 티켓은 Dev Team의 Alice, Bob, Charlie에게 에스컬레이션해야 해요. 이런 티켓이 잘 진행되고 있는지 계속 지켜보는 게 중요해요.
티켓이 Dev Team 구성원 중 한 명에게 할당됐는지 확인하는 헬퍼 함수 assignedToDevTeam을 정의해요.
함수 인자에서 구조 분해를 사용해 Ticket 타입에서 레코드와 필요한 필드를 추출해요.
그다음 하나의 case 문으로 티켓이 Alice, Bob, Charlie 중 누구에게 할당됐는지 확인하는데, 재귀 패턴 매칭으로 각자의 사용자 이름을 추출하고 리터럴 패턴 매칭으로 일치시켜요.
assignedToDevTeam
(Ticket
{ status = InProgress
, createdBy = ( User "Bill", 2 )
, assignedTo = Just (User "Alice")
, comments = [ ( User "Bill", "What's an 'undefined'?" ) ]
}
)
-- => True
때로는 여러 티켓을 다른 IT 직원에게 다시 할당해야 해요.
이 작업을 도와줄 assignTicketTo 함수를 정의해요. 이 함수는 티켓을 할당할 사용자와 티켓, 두 개의 인자를 받아요.
티켓의 상태가 New라면 새 사용자에게 할당하고 상태를 InProgress로 바꿔요.
다른 상태라면 티켓을 새 사용자에게 그냥 다시 할당해요. 단, 상태가 Archived인 티켓은 전혀 수정하면 안 돼요.
함수 인자에서 구조 분해를 사용해 Ticket 타입에서 레코드와 필요한 필드를 추출하고, as 키워드로 레코드 전체를 그대로 잡아 둬요.
하나의 case 문으로 상태를 패턴 매칭하고, as 키워드로 잡아 둔 원본 레코드를 수정해 알맞은 티켓을 반환해요.
assignTicketTo (User "Danny")
(Ticket
{ status = New
, createdBy = ( User "Jesse", 3 )
, assignedTo = Just (User "Alice")
, comments = [ ( User "Jesse", "I've been waiting for 6 months!!" ) ]
}
)
-- => Ticket
-- { status = InProgress
-- , createdBy = ( User "Jesse", 3 )
-- , assignedTo = Just (User "Danny")
-- , comments = [ ( User "Jesse", "I've been waiting for 6 months!!" ) ]
-- }
Exercism에 가입하고 Elm 트랙을 개념 28개연습 문제 110개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.