📜 ⬆️ ⬇️

Easy walk from functor through monad to arrow


Let's take a walk through the chain of Pointed, Functor, Applicative Functor, Monad, Category, Arrow, during which I will try to show that all this is not in order to blow the brain, but to solve very real problems, moreover, existing not only in haskell. Most of the code is written in C #, but I think without his knowledge it will be possible to understand what's what.

Examples


Although the examples use primitive operations on int, if you prefer, you can think of something more vital in their place, for example, in an applicative functor, you can consider this
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, .

')

Source: https://habr.com/ru/post/151703/


All Articles