Pointed、Functor、Applicative Functor、Monad、Category、Arrowのチェーンに沿って歩きましょう。その間、これらすべてが脳を爆破するために発明されたのではなく、さらにhaskellに存在する非常に現実的な問題を解決するために発明されたことを示します。 ほとんどのコードはC#で記述されていますが、彼の知識がなくても、何が何であるかを理解できると思います。
例
例ではintでプリミティブ操作を使用していますが、たとえば、適用可能なファンクターでは、このオプションを検討することができます。
Maybe<int> userId = UserService.GetUserId(email)
Maybe<int> productId = ProductService.GetProductId(productCode)
Maybe<int> discount = Maybe<int>.Nothing;
if(userId.HasValue && porductId.HasValue)
discount = new Maybe(DiscountService.GetDiscount(userId.Value, productId.Value));
, , Maybe, , null .
public class Maybe<T> {
public static readonly Maybe<T> Nothing = new Maybe<T>();
public T Value { get; private set; }
public bool HasValue { get; private set; }
private Maybe() { }
public Maybe(T value) {
Value = value;
HasValue = true;
}
public override string ToString() {
if (HasValue)
return Value.ToString();
return "Nothing";
}
}
Maybe<User> GetUserById(int id){...}
Maybe<A>
? , A
Maybe<A>, int -> Maybe<int>
, , — .
Pointed
:
A -> Maybe<A>
, .
static class Pointed {
public static Maybe<A> Pure<A>(this A value) {
return new Maybe<A>(value);
}
}
C#, this
void Pointed() {
int x = 1;
Maybe<int> y = x.Pure();
}
Maybe.
, , , A → B, . ? , Maybe , , , , Nothing, null. DRY .
static class Functor {
public static Maybe<B> Map<A,B>(this Maybe<A> maybe, Func<A,B> f) {
if(maybe.HasValue) return f(maybe.Value).Pure();
return Maybe<B>.Nothing;
}
}
void Functor() {
Func<int, int> inc = y => y + 1;// int -> int
var x = 1.Pure();// Maybe
var r = x.Map(inc); // , r == Some(2)
}
.
, (A,B) → C? map . , — . add(x,y) = x + y; inc = add(1); inc(10) == 11;
Func .
static class CurryFunc {
public static Func<T1,Func<T2,R>> Curry<T1,T2,R>(this Func<T1,T2,R> f) {
return x => y => f(x, y);
}
}
void Curry() {
Func<int,int, int> add = (y,z) => y + z;
var inc = add.Curry()(1);
var r = inc(1); // r == 2
}
apply .
static class Applicative {
public static Maybe<B> Apply<A,B>(this Maybe<Func<A,B>> f, Maybe<A> maybe) {
if(f.HasValue) return maybe.Map(f.Value); // , Map
return Maybe<B>.Nothing;
}
}
void Applicative() {
Func<int, int, int> addF = (y,z) => y + z;
var add = addF.Curry();
var x1 = 1.Pure();
var x2 = 2.Pure();
var r = add.Pure().Apply(x1).Apply(x2); // Maybe , r == Some(3)
Func<int,int,int> f = DiscountService.GetDiscount;
var discount = f.Curry().Pure().Apply(userId).Apply(productId); //
}
, curry.
Func<int, int, int, int, int> addF = (a,b,c,d) => a + b + c + d;
addF.Curry().Apply(x1).Apply(x2).Apply(x3).Apply(x4);
, ,
A → Maybe GetUserById :: int -> Maybe<User>
map . Maybe<Maybe<B>>
,
static class Monad {
public static Maybe<B> Bind<A,B>(this Maybe<A> maybe, Func<A,Maybe<B>> f) {
if(maybe.HasValue) return f(maybe.Value);
return Maybe<B>.Nothing;
}
}
void Monad() {
Func<int, Maybe<int>> solve = y => y == 0 ? Maybe<int>.Nothing : (y + 1).Pure();
var x = 1.Pure();
var y = 0.Pure();
var r1 = x.Bind(solve); // r1 == Some(2)
var r2 = y.Bind(solve); // r2 == Nothing
}
, , Maybe.
. Maybe .
? , ,
f = length . filter (>3) . map (+1) . skip 3
(.) .
f - , 1 , .
, .
, . . , . . , , . , f: A → B, g:B → C f compose g: A → C. , , .
. .
static class Category {
public static Func<A,C> Compose<A,B,C>(this Func<B,C>f1, Func<A,B> f2) {
return x => f1(f2(x));
}
}
void Category() {
Func<int, int> f1 = y => y + 1;
Func<int, int> f2 = y => y * 2;
var fr = f2.Compose(f1);
var r = fr(1);// r == 4
}
A → Maybe<B>, B → Maybe<C>
.
A → Maybe<B>
. , . - A → M<B>
class Kleisli<A,B> {
public Func<A,Maybe<B>> Func { get; set; }
public Kleisli(Func<A,Maybe<B>> f) {
Func = f;
}
}
.
static class Category {
public static Kleisli<A,B> ToKleisli<A,B>(this Func<A,Maybe<B>> f) {
return new Kleisli<A, B>(f);
}
public static Kleisli<A,C> Compose<A,B,C>(this Kleisli<B,C> f1,
Kleisli<A,B> f2) {
return ToKleisli<A,C>(x => f2.Func(x).Bind(f1.Func));
}
}
void Category2() {
Func<int, Maybe<int>> f1 = y => (y + 1).Pure();
Func<int, Maybe<int>> f2 = y => (y * 2).Pure();
var fr = f2.ToKleisli().Compose(f1.ToKleisli());
var r = fr.Func(1); //r == Some(4)
}
, f2.Compose(f1)
.
. -> B, B -> Maybe<C>
. ,
static class Arrow {
public static Kleisli<A,B> Arr<A,B>(this Func<A,B> f) {
return Category.ToKleisli<A,B>(x => f(x).Pure());//
}
}
void Arrow() {
Func<int, int> f1 = y => y + 1;
Func<int, int> f2 = y => y * 10;
Func<int, Maybe<int>> fm1 = y => (y * 2).Pure();
Func<int, Maybe<int>> fm2 = y => (y - 5).Pure();
var fa1 = f1.Arr();
var fa2 = f2.Arr();
var fk1 = fm1.ToKleisli();
var fk2 = fm2.ToKleisli();
var fr = fk2.Compose(fa1).Compose(fk1).Compose(fa2);
var r1 = fr.Func(2); // r1 == Some(36)
// var r1 = f2.Compose(f1.Arr()).Compose(f1).Compose(f2.Arr())(2)
}
: , .
, . , compose , : (&&&) - , (***) - . .
, , . , C#. , A -> List<A>
, ~90% , . , , Maybe M<A>
M Maybe<A> List<A>
.
, http://anton-k.github.com/ru-haskell-book/book/toc.html, , , ( ) .
haskell
import Prelude hiding (Functor,map,Monad)
class Pointed f where
pure :: a -> f a
instance Pointed Maybe where
pure = Just
class Functor f where
map :: (a -> b) -> f a -> f b
instance Functor Maybe where
map f (Just x) = Just (f x)
map f Nothing = Nothing
class (Functor f, Pointed f) => Applicative f where
apply :: f (a -> b) -> f a -> f b
instance Applicative Maybe where
apply Nothing _ = Nothing
apply (Just f) something = map f something
class Applicative f => Monad f where
bind :: f a -> (a -> f b) -> f b
instance Monad Maybe where
bind (Just x) k = k x
bind Nothing _ = Nothing
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
class Category cat where
compose :: cat b c -> cat a b -> cat a c
instance Category (->) where
compose f g = \x -> f (g x)
instance Monad m => Category (Kleisli m) where
compose (Kleisli f) (Kleisli g) = Kleisli (\b -> bind (g b) f)
class Category a => Arrow a where
arr :: (b -> c) -> a b c
instance Arrow (->) where
arr f = f
instance Monad m => Arrow (Kleisli m) where
arr f = Kleisli (compose pure f)
Upd: .
:
Maybe<int> userId = UserService.GetUserId(email);
Maybe<int> productId = ProductService.GetProductId(productCode);
Maybe<int> discount = Maybe<int>.Nothing;
if(userId.HasValue && porductId.HasValue)
discount = new Maybe(DiscountService.GetDiscount(userId.Value, productId.Value));
? if, .
:
Func<int,int,int> f = DiscountService.GetDiscount;
Maybe<int> discount = f.Curry().Pure().Apply(userId).Apply(productId);
? , userId productId, .