2022. 11. 7. 00:00ㆍFunctional Programming/Category Theory
참고 : https://blog.ploeh.dk/2018/03/19/functors-applicatives-and-friends/
Applicative Functor
일반적인 Functor (fmap) 가 하나의 argument 를 가지고 사용 되는 Functor 라면
Applicative Functor 은 두개의 argument 를 가지고 fmap 하려는 경우 사용 하는 Functor 이다.
이항 연산을 예로 들면
두개의 인자를 받아야 하는데 fmap 같은 경우는 하나의 인자만 받을수 있으니 이것이 힘들다
그래서 이 함수 자체를 감싼 Functor 를 만들어 사용하면 해결 된다.
add(left)(right) = left + right
map(add)([1,2,3])
-- [ add(1),
-- add(2),
-- add(3) ]
위와 같이 하면 left 에는 1 이 들어가지만 right 에는 어떤 값도 넣을수 없으니 불가능 하다
이럴 경우 add1 이라는 partial 함수를 만들어 적용 할 수 있다.
또한 다른 방법들도 있는데 Haskell 에서는 아래와 같다.
pure(input) = [input]
apply(functions)(list) =
[ element | function <- functions,
element <- map(function)(list)
]
apply(pure(add1))([1,2,3])
-- [2,3,4]
apply(apply(pure(add))([1,2,3]))([4,5,6])
-- [5,6,7,6,7,8,7,8,9]
apply(map(add)([1,2,3]))([4,5,6])
-- [5,6,7,6,7,8,7,8,9]
함수를 감싼 Functor
List 안에 함수가 들어 있다고 생각하면 쉽다.
결과적으로 이 functor 의 의해 나오는 값(??) 은
함수들을 적용하여 나올수 있는 모든 경우의 수가 나온다.
f1 , f2, f3 를 특정 List 에 적용하면
f1 을 적용한 값 , f2 를 적용한 값 , f3 를 적용한 값을 볼수 있다.
카드 게임에서 나올수 있는 목록의 종류
정해진 문자열을 이용하여 만들수 있는 password 의 종류
등등 이런 분야에 응용하여 사용할 수 있다.
추상화 하면 아래와 같은 형태일 것이다.
public static class ApplicativeFunctors
{
public static IEnumerable<TResult> Apply<T, TResult>(
this IEnumerable<Func<T, TResult>> selectors,
IEnumerable<T> source)
{
return selectors.SelectMany(source.Select);
}
}
요런식으로 사용할 수 있다.
var func1 = (int x) => x + 1;
var func2 = (int x) => x + 2;
var func3 = (int x) => x + 3;
var sourceList = new List<int> { 1, 2, 3, 4, 5 };
var applicativeFunctor = new List<Func<int, int>>();
applicativeFunctor.Add(func1);
applicativeFunctor.Add(func2);
applicativeFunctor.Add(func3);
var datas = applicativeFunctor.Apply(sourceList);
foreach (var item in datas)
WriteLine(item);
Bi Functor
일반적으로 하나의 Generic Type 을 mapping 하는 function(??) 을 Functor 라고 한다
두개의 Generic Type 을 mapping 하는 Functor 를 BiFunctor 라고 할 수 있다.
Bar<Left, Right> 라고 정의 했을때 Left 를 먼저 mapping 하고 Right 를 mapping 하는 경우가 있고
Right 를 먼저 mapping 하고 Left 를 mapping 하는 경우도 있다.
Anyway ... bimap 은 두 값을 한번에 mapping 한다.
위의 예에서는 Bar<string, int> 를 Bar<bool, DateTime> 으로 변환한 것이다.
Bifunctor 의 예는 Either<Left,Right> 와 Tuple<T,U> 이 있다.
Tuple 을 이용하여 Bi Functor 를 구현해 보자
public static class TupleExtensions
{
public static Tuple<T, U2> Select<T, U1, U2>(
this Tuple<T, U1> source,
Func<U1, U2> selector)
{
return SelectSecond(source, selector);
}
public static Tuple<T, U2> SelectSecond<T, U1, U2>(
this Tuple<T, U1> source,
Func<U1, U2> selector)
{
return Tuple.Create(source.Item1, selector(source.Item2));
}
public static Tuple<T2, U> SelectFirst<T1, T2, U>(
this Tuple<T1, U> source,
Func<T1, T2> selector)
{
return Tuple.Create(selector(source.Item1), source.Item2);
}
public static Tuple<T2, U2> SelectBoth<T1, T2, U1, U2>(
this Tuple<T1, U1> source,
Func<T1, T2> selector1,
Func<U1, U2> selector2)
{
return source.SelectFirst(selector1).SelectSecond(selector2);
}
}
var t = Tuple.Create("foo", 42);
// get first character
WriteLine(t.SelectFirst(s => s.First()));
// even check
var actual = from i in t
select i % 2 == 0;
//t.SelectSecond(i=>i % 2 == 0);
WriteLine(actual.ToString());
WriteLine(t.SelectBoth(s => s.First(), i => i % 2 == 0));
관련영상
'Functional Programming > Category Theory' 카테고리의 다른 글
Monad (0) | 2022.11.21 |
---|---|
Contravariant Functor, Profunctor (0) | 2022.11.14 |
Functor (0) | 2022.10.31 |
Product and CoProduct (0) | 2022.10.24 |
Kleisli Catogories (0) | 2022.10.17 |