2

Je tente d'implémenter des foncteurs en Javascript sans utiliser de types de conteneur ([]/{}). Par conséquent, j'utilise uniquement des fonctions pures d'ordre supérieur pour les construire:Les foncteurs ou les monades peuvent-ils être exprimés uniquement avec des fonctions d'ordre supérieur?

const option = x => f => isAssigned(x) ? option(f(x)) : none; 
 

 
const none = option(null); 
 

 

 
const isAssigned = x => x !== null && x !== undefined; 
 
const inc = x => x + 1; 
 
const sqr = x => x * x; 
 
const head = xs => xs[0] 
 
const log = x => (console.log(x), x); 
 

 

 
option(head([4])) (inc) (sqr) (log); // 25 
 

 
option(head([])) (inc) (sqr) (log); // not executed

option prend une valeur et une fonction pure, soulève la fonction dans son contexte, l'applique à la valeur et renvoie la aboutir dans le même contexte. Je suppose que c'est un foncteur. Cependant, il ne suit pas le prototcol de foncteur en Javascript, que chaque foncteur doit posséder une fonction de carte sur son prototype.

option peut apparemment être étendue à un type comme monade (au moins il se comporte comme dans mon exemple):

const option = x => f => isAssigned(x) ? option(f(x)) : none; 
 

 
const option_ = x => f => isAssigned(x) ? flatten(option(f(x))) : none; 
 

 
const none = option(null); 
 

 
const of = x => option(x); // return 
 

 
const flatten = F => { // it gets a bit ugly here 
 
    let y; 
 

 
    F(z => (y = z, z)); 
 
    return y; 
 
}; 
 

 
// auxiliary functions 
 

 
const compn = (...fs) => x => fs.reduceRight((acc, f) => f(acc), x); 
 

 
const getOrElse = x => F => { 
 
    let y; 
 

 
    F(z => (y = z, z)); 
 
    return isAssigned(y) ? y : x; 
 
}; 
 

 
const isAssigned = x => x !== null && x !== undefined; 
 
const log = prefix => x => (console.log(prefix, x), x); 
 
const head = xs => xs[0]; 
 
const head_ = xs => option(xs[0]); 
 
const sqr = x => x * x; 
 

 
// some data 
 

 
const xs = [5], 
 
ys = []; 
 

 
// run 
 

 
const w = option(xs) (head), 
 
x = option(ys) (head), 
 
y = option_(xs) (head_), 
 
z = option_(ys) (head_); 
 

 
log("square head of xs:") (compn(sqr, getOrElse(1)) (w)); // 25 
 

 
log("square head of ys:") (compn(sqr, getOrElse(0)) (x)); // 0 
 

 
log("square head_ of xs:") (compn(sqr, getOrElse(0)) (y)); // 25 
 

 
log("square head_ of ys:") (compn(sqr, getOrElse(0)) (z)); // 0

fourni option est en fait un foncteur ma question est: Est-il possible d'exprimer chaque foncteur/monade uniquement avec des fonctions (pures) d'ordre supérieur, où les résultats des calculs contextuels (ou effectifs) sont conservés dans la pile d'appels?

+0

pourquoi voulez-vous faire cela? Si "haskell dans le navigateur" est votre objectif, il existe d'autres solutions – niceman

+0

@ftor Voulez-vous dire quelque chose comme [Codage de l'église] (https://en.wikipedia.org/wiki/Church_encoding)? – phg

+0

@phg la base théorique, merci. Donc c'est théoriquement possible. Je me demande encore s'il y a des problèmes pratiques pour les implémenter en Javascript. Jusqu'à présent 'option' n'a pas de type (proto). Comment puis-je associer des fonctions (comme 'of') avec la bonne monade? De plus, 'option' est seulement une union. Il n'y a pas de balises ni de moyens de refléter les types possibles adoptés par le syndicat. – ftor

Répondre

1

Bien sûr. Les fonctions peuvent à peu près tout faire. Quoi qu'il en soit, vous avez demandé si tu sais que je vais essayer de mon mieux pour livrer^_^

En dehors de quelques primitives JS (*, +, Number et String) pour démontrer la fonctionnalité, ci-dessous, vous ne verrez :

  1. Variables
  2. abstractions lambda
  3. applications

Ce n'est pas co l'incidence que ce sont (essentiellement) les 3 blocs de construction fondamentaux du λ-calcul.

const identity = x => x 
 
const fromNullable = x => x == null ? None : Option(x) 
 

 
const Option = (value) => k => { 
 
    const join =() => value 
 
    const map = f => Option(f(value)) 
 
    const bind = f => f(value) 
 
    const ap = m => optionMap(value)(m) 
 
    const fold = f => f(value) 
 
    return k (value, join, map, bind, ap, fold) 
 
} 
 

 
const None =() => k => { 
 
    const join = identity 
 
    const map = f => None() 
 
    const bind = f => None() 
 
    const ap = m => None() 
 
    const fold = f => f(null) 
 
    return k (null, join, map, bind, ap, fold) 
 
} 
 

 
const optionJoin = m => m((value, join, map, bind, ap, fold) => join()) 
 
const optionMap = f => m => m((value, join, map, bind, ap, fold) => map(f)) 
 
const optionBind = f => m => m((value, join, map, bind, ap, fold) => bind(f)) 
 
const optionAp = n => m => m((value, join, map, bind, ap, fold) => ap(n)) 
 
const optionFold = f => m => m((value, join, map, bind, ap, fold) => fold(f)) 
 

 
optionFold (console.log) (Option(5)) // 5 
 
optionFold (console.log) (None()) // null 
 

 
optionFold (console.log) (optionMap (x => x * 2) (Option(5))) // 10 
 
optionFold (console.log) (optionMap (x => x * 2) (None()))// null 
 

 
optionFold (console.log) (optionAp(Option(3)) (Option(x => x + 4))) // 7 
 
optionFold (console.log) (optionAp(Option(3)) (None())) // null 
 

 
optionFold (console.log) (optionBind(x => Option(x * x)) (Option(16))) // 256 
 
optionFold (console.log) (optionBind(x => Option(x * x)) (None())) // null 
 

 
optionFold (console.log) (optionJoin (Option(Option('hi')))) // 'hi' 
 
optionFold (console.log) (optionJoin (Option(None())))// null

+0

J'attends avec impatience la discussion, si vous êtes intéressé bien sur^_^ – naomik

+0

Vous mettez toute l'API dans le type 'Option'. Ça a du sens. Je vais jouer un peu avec ça et revenir vers vous plus tard. – ftor

+0

ouais le traiter comme un type de classe - commodément tho, à la fois Option et Aucun type peuvent définir leurs propres "méthodes" – naomik