MonetJS - Maybe Monad

Donc la derniĂšre fois nous avons parlĂ© de la monade Identity avec le framework monet.js (monet.js - Maybe Monad). Pour ce Dimanche matin, je vais faire une 2Ăšme partie un peu plus courte oĂč je vais vous parler de Maybe.

Maybe?

Qu’est-ce qu’une Maybe? Pensez Ă  un type qui peut changer de type 😜 ou Ă  un type qui a 2 sous types. En fait une Maybe est une “boĂźte” 📩 qui contient une valeur (comme un container donc), et elle (la boĂźte) peut ĂȘtre de type None, c’est Ă  dire que sa valeur est nulle ou de type Some, c’est Ă  dire que sa valeur n’est pas nulle. ConcrĂštement la Maybe est faite pour aider Ă  gĂ©rer les null(s) et nous Ă©viter pas mal d’exceptions/erreur, 
 En fait c’est la mĂȘme chose que le type Option en Scala ou Java.

Utilisation

Voici quelques exemples simples pour montrer le fonctionnement de base:

Constructor(s)

let maybe = monet.Maybe.Some(42)

console.log(
	maybe.isSome(), // == true
	maybe.isNone() // == false
)

ou bien:

let maybe = monet.Maybe.None()

console.log(
	maybe.isSome(), // == false
	maybe.isNone() // == true
)

Mais lorsque l’on ne sait pas à l’avance si l’on va avoir null ou pas on utilise fromNull (une factory de Maybe):

let maybe = monet.Maybe.fromNull(42)

console.log(
	maybe.isSome(), // == true
	maybe.isNone() // == false
)

Obtenir la valeur d’une Maybe: orSome

Une Maybe n’a pas de mĂ©thode get, Ă  la place nous avons orSome:

let maybe = monet.Maybe.fromNull("😄")

console.log(
	maybe.orSome("😡") // maybe.orSome("😡") == 😄
)

ou dans le cas d’un None:

let maybe = monet.Maybe.fromNull(null)

console.log(
	maybe.orSome("😡") // maybe.orSome("😡") == 😡
)

Obtenir une Maybe d’une Maybe: orElse

orElse ne retourne pas une valeur mais une Maybe:

dans le cas d’un Some

let maybe = monet.Maybe.fromNull("😄")
let newMaybe = maybe.orElse(monet.Maybe.Some("đŸ€ą"))

console.log(
	newMaybe, // { isValue: true, val: '😄' }
	newMaybe.isSome() // == true
)

dans le cas d’un None

let maybe = monet.Maybe.fromNull(null)
let newMaybe = maybe.orElse(monet.Maybe.Some("đŸ€ą"))

console.log(
	newMaybe, // { isValue: true, val: 'đŸ€ą' }
	newMaybe.isSome() // == true
)

⚠ Donc orElse retourne la Maybe initiale si c’est un Some ou la Maybe proposĂ©e si un None.

Ok, et en vrai, à quoi ça pourrait me servir?


 Pensez à une recherche en base de données:

let users = [
    {id:1, name:"bob", avatar:"đŸŒ"}
  , {id:2, name:"sam", avatar:"đŸ»"}
  , {id:3, name:"jane", avatar:"🐰"}
  , {id:4, name:"john", avatar:"đŸ±"}
]

let getUserById = id => monet.Maybe.fromNull(users.find(u => u.id == id))

console.log(
	getUserById(3) // { isValue: true, val: { id: 3, name: 'jane', avatar: '🐰' } }
)
console.log(
	getUserById(6) // { isValue: false, val: null }
)

Dans le 1er cas j’obtiens un Some, sans le 2Ăšme un None
 ok 
 Mais comme c’est une monade, nous avons la mĂ©thode map❗ et on peut faire plein de choses sympas

Maybe et map()

Avant, pour obtenir l’avatar d’un utilisateur que l’on cherche par son id, on aurait fait quelaue chose comme ceci:

users.find(u => u.id == 6).avatar

Et comme l’utilisateur n’existe pas, on obtient un joli:

TypeError: Cannot read property 'avatar' of undefined

Qui n’a jamais oubliĂ© de tester si il avait rĂ©cupĂ©rĂ© quelque chose ou pas avant de jouer avec dans son code ⁉. Mais maintenant, vous pouvez faire ceci:

let getUserById = id => monet.Maybe.fromNull(users.find(u => u.id == id))
let getAvatar = u => u.avatar

console.log("display avatar of 2:",
	getUserById(2).map(getAvatar).orSome("đŸ‘»")
)

console.log("display avatar of 6:",
	getUserById(6).map(getAvatar).orSome("đŸ‘»") // be careful Number six doesn't exist ... I'm not a number, I'm a free man!
)

et vous obtiendrez ceci:

display avatar of 2: đŸ»
display avatar of 6: đŸ‘»

et sans “plantage” â—ïžđŸ˜Š

  • getUserById me retourne une monade Maybe M1
  • avec map je peux appliquer une transformation Ă  la valeur M1 (sans la modifier)
  • et obtenir une nouvelle monade Maybe M2
  • sur laquelle je peux faire un orSome
  • ✌ donc plus de plantage idiot dans une recherche dans une base de donnĂ©es, collections, 


Un dernier pour la route đŸ·

Une chose que j’adore ❀ avec la Maybe, c’est le catamorphisme đŸ€Š (allez faire un tour ici https://fr.wikipedia.org/wiki/Catamorphisme ça pique đŸŒ” hein?). Plus simplement (et mĂȘme si Ă©tymologiquement parlant c’est tirĂ© par les 💇), pensez Ă  catastrophe.

Dans monet.js la Maybe a une méthode cata qui va vous permettre de gérer les catastrophe. Cette méthode prend 2 arguments (en fait 2 callbacks ou 2 closures, appelez les comme vous le sentez), cata(left(), right(value)):

  • le 1Ăšr callback, que l’on appellera left (l’argument le plus Ă  gauche) qui ne prend aucune valeur en argument et qui est appelĂ© quand la Maybe est un None
  • le 2Ăšme callback, que l’on appellera right, qui prend comme argument la valeur de la Maybe quand c’est un Some

⚠ right est aussi un moyen mĂ©motechnique: right comme juste, “c’est ok, tu as tout juste 👍”.

Mais avec un petit bout de code ce sera plus parlant, rappelez vous de notre getUserById qui nous retourne une Maybe:

console.log(
	getUserById(3).cata(
		()=> {
			return "this 😡 does not exist"
		},
		(user) => {
			return `Hello ${user.avatar}`
		}
	)
)

Dans ce cas là, nous obtiendrons: Hello 🐰

console.log(
	getUserById(6).cata(
		()=> {
			return "this 😡 does not exist"
		},
		(user) => {
			return `Hello ${user.avatar}`
		}
	)
)

Dans ce cas là, nous obtiendrons: this 😡 does not exist

Fin de ce chapitre. Maybe est trùs pratique, mais dùs fois, on aurait besoin qu’elle en fasse un tout petit peu plus. Donc la prochaine fois, je vous parlerais de Either.

Bon Dimanche 🍗 🍰 đŸ·

blog comments powered by Disqus

Related posts