본문 바로가기

프로그래밍 기술 노트/Functional Study

[Monad] 내멋대로 Monad 이해하기 - Kleisli🐟

모나드 다 제끼고, 아래와 같은 함수가 있다고 하자

type User = {id:int}

let nonSafeParse (str:string) =
    match Int32.TryParse str with
    | true, value -> value
    | _           -> raise (ArgumentException("Invalid input"))

let nonSafeCreateUser (id:int) =
    if id = 0 then raise (ArgumentException("Invalid input")) else {id=id}

let nonSafeParseAndCreateUser = nonSafeParse >> nonSafeCreateUser

let nonSafeWay1 = "1" |> nonSafeParse |> nonSafeCreateUser
let nonSafeWay2 = "1" |> (nonSafeParse >> nonSafeCreateUser)
let nonSafeWay2' = "1" |> nonSafeParseAndCreateUser

 

고차함수를 이용해서 함수를 합성하게 되면, 

|> 과 같이 값을 계속 넘겨주는 "Pipeline" 방식과

>> 과 같이 함수를 합성하는 "Compose" 방식을 사용할 수 있다.

 

보통, Chaining 할떄는 Pilelining 하고, 별도의 함수를 만들때는 Compose 해서 만드는 경향이 있는데, 뭐 대충 상황에 맞게 쓰면 된다.

그런데, 만약 해당 함수가 모나드로 디자인 되어있다면?

[Monad] 내멋대로 Monad 이해하기 - Bind (FlatMap), Monad (tistory.com)

 

[Monad] 내멋대로 Monad 이해하기 - Bind (FlatMap), Monad

C#이나 JAVA 에서 보통 함수를 어떻게 만들까? 예를 들어서 number를 받아서 User를 만드는 함수라고 하고한다면... public class User { public int id { get; set; } } public User CreateUser (int id) { return new User { id = id

see-ro-e.tistory.com

Bind (FlatMap) 라는것은 Monad 식 Chaining 일 뿐이다. 

let tryParse (str:string) =
    match Int32.TryParse str with
    | true, value -> Some value
    | _           -> None

let createUser (id:int) =
    if id = 0 then None else Some {id=id}

let way1 = "1" |> tryParse |> Option.bind createUser

이렇게 Bind 를 이용해서 모나드의 Chaining 을 할수 있도록 한다.

 

그런데 Compose 는..?

let parseAndCreateUser = tryParse >> Option.bind createUser

위 처럼 만들수 있다. 그런데 Bind랑 합성하고 넘기면 되는데.. 잘 생각해보면 위 함수는 함수끼리 Compose 한게 아니라, 그냥 Manunally 하게 Binding 한것 같은 느낌이 든다.

마치 (모나드가 아닌) 일반 함수에서 아래처럼 쓴것 같은 느낌이다.

// let nonSafeParseAndCreateUser = nonSafeParse >> nonSafeCreateUse 이게 아니라
let nonSafeParseAndCreateUser x = (nonSafeParse x) |> nonSafeCreateUser

즉 일반함수에도 Compose 가 있듯이 Monad 의 Compose 가 따로 있었으면 좋겠다.

대충 그게 `Kleisli composition` 이다.

일반적으로 Operator 로 만들면 >=> 기호를 쓰는데, 물고기 처럼 생겨서 (??) fish Operator 라고 부른다

// fish operator (Kleisli composition)
let inline (>=>) a b x =
    match a x with
    | Some y -> b y
    | None   -> None

//let parseAndCreateUser = tryParse >> Option.bind createUser
let parseAndCreateUser = tryParse >=> createUser

let way1 = "1" |> tryParse |> Option.bind createUser
let way2 = "1" |> (tryParse >=> createUser)
let way2' = "1" |> parseAndCreateUser

 

즉 A -> B 꼴 함수가 아니라

`A -> F[B]`  꼴 함수가 합성되는데 

A -> F[B] 이 시그니쳐 자체를 Type 으로 만들면  Kleisli  타입이다.

(대충 bind 쓸수있으면 Monad 이듯이, 대충 Kleisli Compose 를 쓸수있으면  Kleisli 타입이다)

 

728x90