Sanctuary Js and Defining a Contravariant Functor - javascript

I am trying this from scratch learning about Contravariants and deeper knowledge of Sanctuary. The code "works" but again I don't have the types exactly right.
Here is the Contravariant
const {contramap: contramapFl, extract } = require('fantasy-land');
const getInstance = (self, constructor) =>
(self instanceof constructor) ?
self :
Object.create(constructor.prototype) ;
// Contra a ~> g: a -> ?
const Contra = function(g){
const self = getInstance(this, Contra)
// :: F a ~> b -> a -> F b [ b -> a -> ? ]
self[contramapFl] = f => Contra( x => g(f(x)) )
self[extract] = g
self['##type'] = 'fs-javascript/contra'
return Object.freeze(self)
}
// UPDATE adding type to constructor
Contra['##type'] = 'fs-javascript/contra'
And my attempt to get the types right
const $ = require('sanctuary-def');
const type = require('sanctuary-type-identifiers');
const Z = require('sanctuary-type-classes') ;
const isContra = x => type (x) === 'fs-javascript/contra'
const ContraType = $.UnaryType(
'fs-javascript/contra',
'http://example.com/fs-javascript#Contra',
isContra,
x => [x[extract]])($.Unknown)
Then my test
const {create, env} = require('sanctuary');
const {contramap} = create({checkTypes: true, env: env.concat(ContraType) });
const isEven = Contra(x => x % 2 === 0) ;
console.log(Z.Contravariant.test(isEven)) // => true
const isLengthEvenContra = contramap(y => y.length, isEven)
const isStringLengthEven = isLengthEvenContra[extract]
console.log(isStringLengthEven("asw")) //=> ERROR
TypeError: Type-variable constraint violation
contramap :: Contravariant f => (b -> a) -> f a -> f b
^
1
1) "fs-javascript/contra" :: String
f => Contra( x => g(f(x)) ) :: Function, (c -> d)
x => x % 2 === 0 :: Function, (c -> d)
Since there is no type of which all the above values are members, the type-variable constraint has been violated.
If I disable the type checking then it works as expected, so logically it appears to be stitched together properly. I defined my own version of contramap
const def = $.create({ checkTypes: true, env: $.env.concat(ContraType) });
const contramap2 =
def('contramap2', {}, [$.Unknown, ContraType, ContraType],
(f, x) => {
const z = x[contramapFl](f)
return z
}
)
I then rerun the test:
const isEven = Contra(x => x % 2 === 0) ;
console.log(Z.Contravariant.test(isEven)) // => true
const isLengthEvenContra = contramap2(y => y.length, isEven)
const isStringLengthEven = isLengthEvenContra[extract]
console.log(isStringLengthEven("asw")) //=> false
So withstanding the discussion as to whether the contravaiant functor is the best approach to this problem (learning exercise), the question is how, when defining my own implementation of a contravariant, can I use sanctuary's contramap function with the type checking enabled.
after updating by adding the code:
Contra['##type'] = 'fs-javascript/contra'
changed the error to:
TypeError: Type-variable constraint violation
contramap :: Contravariant f => (b -> a) -> f a -> f b
^ ^
1 2
1) 3 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, NonNegativeInteger, ValidNumber
2) x => x % 2 === 0 :: Function, (c -> d)
Since there is no type of which all the above values are members, the type-variable constraint has been violated.
// Contra (Integer -> Boolean)
const isEven = Contra(x => x % 2 === 0) ;
// String -> Integer
const strLength = y => y.length
// I Think: Contra (String -> (Integer -> Boolean))
const isLengthEvenContra = contramap(strLength, isEven)
// (String -> (Integer -> Boolean))
const isStringLengthEven = isLengthEvenContra[extract]
My understanding of the contravariant functor was that it pre-composed the function within it, with the function passed in via contramap. So if the contravariant contained the function f and it is contramap with g it returns a new contravariant functor wrapping x = g(f(x)) Have i misunderstood this (too)

Here's the boilerplate for defining a custom type and including it in the Sanctuary environment:
'use strict';
const {create, env} = require ('sanctuary');
const $ = require ('sanctuary-def');
const type = require ('sanctuary-type-identifiers');
// FooType :: Type -> Type
const FooType = $.UnaryType
('my-package/Foo')
('https://my-package.org/Foo')
(x => type (x) === 'my-package/Foo#1')
(foo => []);
// foo :: Foo
const foo = {
'constructor': {'##type': 'my-package/Foo#1'},
'fantasy-land/contramap': function(f) {
throw new Error ('Not implemented');
},
};
const S = create ({
checkTypes: true,
env: env.concat ([FooType ($.Unknown)]),
});
S.I (foo);
// => foo
S.contramap (S.I) (foo);
// ! Error: Not implemented

You're composing functions. Sanctuary defines fantasy-land/map and fantasy-land/contramap for Function a b, so there's no need for a Contra wrapping type.
> S.map (S.even) (s => s.length) ('Sanctuary')
false
> S.contramap (s => s.length) (S.even) ('Sanctuary')
false

Related

Algorithm implementation to convert propositional formula into conjunctive normal form in JavaScript?

I saw How to convert a propositional formula to conjunctive normal form (CNF)? but it doesn't go into implementation details. So I was lucky to find this which shows the types:
abstract class Formula { }
class Variable extends Formula { String varname; }
class AndFormula extends Formula { Formula p; Formula q; } // conjunction
class OrFormula extends Formula { Formula p; Formula q; } // disjunction
class NotFormula extends Formula { Formula p; } // negation
class ImpliesFormula extends Formula { Formula p; Formula q; } // if-then
class EquivFormula extends Formula { Formula p; Formula q; }
class XorFormula extends Formula { Formula p; Formula q; }
Then it has this helpful (start of a) function CONVERT:
CONVERT(φ): // returns a CNF formula equivalent to φ
// Any syntactically valid propositional formula φ must fall into
// exactly one of the following 7 cases (that is, it is an instanceof
// one of the 7 subclasses of Formula).
If φ is a variable, then:
return φ.
// this is a CNF formula consisting of 1 clause that contains 1 literal
If φ has the form P ^ Q, then:
CONVERT(P) must have the form P1 ^ P2 ^ ... ^ Pm, and
CONVERT(Q) must have the form Q1 ^ Q2 ^ ... ^ Qn,
where all the Pi and Qi are disjunctions of literals.
So return P1 ^ P2 ^ ... ^ Pm ^ Q1 ^ Q2 ^ ... ^ Qn.
If φ has the form P v Q, then:
CONVERT(P) must have the form P1 ^ P2 ^ ... ^ Pm, and
CONVERT(Q) must have the form Q1 ^ Q2 ^ ... ^ Qn,
where all the Pi and Qi are dijunctions of literals.
So we need a CNF formula equivalent to
(P1 ^ P2 ^ ... ^ Pm) v (Q1 ^ Q2 ^ ... ^ Qn).
So return (P1 v Q1) ^ (P1 v Q2) ^ ... ^ (P1 v Qn)
^ (P2 v Q1) ^ (P2 v Q2) ^ ... ^ (P2 v Qn)
...
^ (Pm v Q1) ^ (Pm v Q2) ^ ... ^ (Pm v Qn)
If φ has the form ~(...), then:
If φ has the form ~A for some variable A, then return φ.
If φ has the form ~(~P), then return CONVERT(P). // double negation
If φ has the form ~(P ^ Q), then return CONVERT(~P v ~Q). // de Morgan's Law
If φ has the form ~(P v Q), then return CONVERT(~P ^ ~Q). // de Morgan's Law
If φ has the form P -> Q, then:
Return CONVERT(~P v Q). // equivalent
If φ has the form P <-> Q, then:
Return CONVERT((P ^ Q) v (~P ^ ~Q)).
If φ has the form P xor Q, then:
Return CONVERT((P ^ ~Q) v (~P ^ Q)).
I translated it to JavaScript below, but am stuck on the AND and OR bits. I want to make sure I get this correct too.
The description of my "data model" / data structure is here.
/*
* Any syntactically valid propositional formula φ must fall into
* exactly one of the following 7 cases (that is, it is an instanceof
* one of the 7 subclasses of Formula).
*
* #see https://www.cs.jhu.edu/~jason/tutorials/convert-to-CNF.html
*/
function convert(formula) {
switch (formula.type) {
case 'variable': return formula
case 'conjunction': return convertConjunction(formula)
case 'disjunction': return convertDisjunction(formula)
case 'negation': return convertNegation(formula)
case 'conditional': return convertConditional(formula)
case 'biconditional': return convertBiconditional(formula)
case 'xor': return convertXOR(formula)
default:
throw new Error(`Unknown formula type ${formula.type}.`)
}
}
function convertConjunction(formula) {
return {
type: 'conjunction',
base: convert(formula.base),
head: convert(formula.head),
}
// CONVERT(P) must have the form P1 ^ P2 ^ ... ^ Pm, and
// CONVERT(Q) must have the form Q1 ^ Q2 ^ ... ^ Qn,
// where all the Pi and Qi are disjunctions of literals.
// So return P1 ^ P2 ^ ... ^ Pm ^ Q1 ^ Q2 ^ ... ^ Qn.
}
function convertDisjunction(formula) {
return {
type: 'disjunction',
base: convert(formula.base),
head: convert(formula.head),
}
// CONVERT(P) must have the form P1 ^ P2 ^ ... ^ Pm, and
// CONVERT(Q) must have the form Q1 ^ Q2 ^ ... ^ Qn,
// where all the Pi and Qi are dijunctions of literals.
// So we need a CNF formula equivalent to
// (P1 ^ P2 ^ ... ^ Pm) v (Q1 ^ Q2 ^ ... ^ Qn).
// So return (P1 v Q1) ^ (P1 v Q2) ^ ... ^ (P1 v Qn)
// ^ (P2 v Q1) ^ (P2 v Q2) ^ ... ^ (P2 v Qn)
// ...
// ^ (Pm v Q1) ^ (Pm v Q2) ^ ... ^ (Pm v Qn)
}
function convertNegation(formula) {
// If φ has the form ~A for some variable A, then return φ.
if (formula.formula.type === 'variable') {
return formula
}
// If φ has the form ~(~P), then return CONVERT(P). // double negation
if (formula.formula.type === 'negation') {
return convert(formula.formula.formula)
}
// If φ has the form ~(P ^ Q), then return CONVERT(~P v ~Q). // de Morgan's Law
if (formula.formula.type === 'conjunction') {
return convert({
type: 'disjunction',
base: {
type: 'negation',
formula: formula.formula.base,
},
head: {
type: 'negation',
formula: formula.formula.head,
}
})
}
// If φ has the form ~(P v Q), then return CONVERT(~P ^ ~Q). // de Morgan's Law
if (formula.formula.type === 'disjunction') {
return convert({
type: 'conjunction',
base: {
type: 'negation',
formula: formula.formula.base
},
head: {
type: 'negation',
formula: formula.formula.head
}
})
}
throw new Error(`Invalid negation.`)
}
function convertConditional(formula) {
// Return CONVERT(~P v Q). // equivalent
return convert({
type: 'disjunction',
base: {
type: 'negation',
formula: formula.base,
},
head: formula.head
})
}
function convertBiconditional(formula) {
// Return CONVERT((P ^ Q) v (~P ^ ~Q)).
return convert({
type: 'disjunction',
base: {
type: 'conjunction',
base: formula.base,
head: formula.head,
},
head: {
type: 'conjunction',
base: {
type: 'negation',
formula: formula.base,
},
head: {
type: 'negation',
formula: formula.head,
},
}
})
}
function convertXOR(formula) {
// CONVERT((P ^ ~Q) v (~P ^ Q)).
return convert({
type: 'disjunction',
base: {
type: 'conjunction',
base: formula.base,
head: {
type: 'negation',
formula: formula.head,
},
},
head: {
type: 'conjunction',
base: {
type: 'negation',
formula: formula.base,
},
head: formula.head,
}
})
}
I have the AND and OR as a pair. So if you write in math like this:
A ∧ B ∧ C ∧ D ∧ E
That would be more like this in code:
A ∧ (B ∧ (C ∧ (D ∧ E)))
But the problem is, we might have arbitrary trees of formulas:
(((A ∧ B) ∧ (C ∧ D)) ∧ (E ∧ F))
Same with the OR. So how would you implement these functions convertDisjunction and convertConjunction, so they can handle that sort of tree data structure?
I tried implementing convertConjunction and convertDisjunction, but I don't think I have it right.
Working around the problem
The problem you raised about the nested conjunction expressions can be worked around by
allowing a formula instance to have more than the base and head operands. I would suggest to allow
conjunctions and disjunctions to have any number of operands and to store them in an array. So A^B^C^D
would be just one conjunction.
I provide an implementation below. It defines one global class Formula which incorporates
the sub classes as static members.
A Formula instance should be considered immutable. All methods that return a formula
either return an existing formula without change, or will create a new formula.
Example use
// Create variables
const P = Formula.getVariable("P");
const Q = Formula.getVariable("Q");
const R = Formula.getVariable("R");
const S = Formula.getVariable("S");
const T = Formula.getVariable("T");
// Build a formula using the variables
// (P^Q^~R)v(~S^T)
const formula = P.and(Q).and(R.not()).or(S.not().and(T));
// ...or parse a string (This will create variables where needed)
const formula2 = Formula.parse("(P^Q^~R)v(~S^T)");
// Get the string representation of a formula
console.log("Formula: " + formula); // (P^Q^~R)v(~S^T)
// Check whether the formula has a CNF structure
console.log("Is it CNF: " + formula.isCnf()); // false
// Create a CNF equivalent for a formula
const cnfFormula = formula.cnf();
console.log("In CNF: " + cnfFormula); // (Pv~S)^(PvT)^(Qv~S)^(QvT)^(~Rv~S)^(~RvT)
// Verify that they are equivalent
console.log("Is equivalent? ", formula.equivalent(cnfFormula)); // true
// Evaluate the formula providing it the set of variables that are true
console.log("When P and T are true, and Q, R and S are false, this evaluates to: ",
formula.evaluate(new Set([P,T]))); // true
There should be no need to use new. The program does not have to be aware of the sub classes.
The static methods on Formula (namely getVariable and parse) will give you a first formula instance,
from which other formulas can be produced.
Be aware that making a CNF can result in large expressions. This code does not attempt to
reduce the size by applying many of the rules available for propositional formulas. The returned CNF will always be
a conjunction of disjunctions, without simplification. So even when the formula is just a single variable,
calling .cnf() on it will turn it into a conjunction of one argument, which in turn is a disjunction of just one argument.
Its string representation will still be the variable's name, as the stringification will not add parentheses for connectives
which only have one argument.
Implementation
class Formula {
#args;
constructor(...args) {
this.#args = args;
}
// Methods to return a formula that applied a connective to this formula
not() {
return new Formula.#Negation(this);
}
and(other) {
return new Formula.#Conjunction(this, other);
}
or(other) {
return new Formula.#Disjunction(this, other);
}
implies(other) {
return new Formula.#Conditional(this, other);
}
// ==== Methods that can be overridden by the subclasses ====
// Evaluate the formula using explicit FALSE/TRUE values.
// A Variable will evaluate to TRUE if and only when it is in
// the Set that is given as argument.
evaluate(trueVariables) {
// Default is undefined: subclass MUST override and return boolean
}
toString() {
// Default: subclass can override
return this.#stringifyArgs().join(this.constructor.symbol);
}
// Return whether this formula is in CNF format
// If level is provided, it verifies whether this formula
// can be at that level within a CNF structure.
isCnf(level=0) {
return false; // Default: subclass can override
}
// Return an equivalent formula that is in CNF format
cnf() {
return this; // Default: subclass MUST override
}
// Get list of all variables used in this formula
usedVariables() {
// Formula.Variable should override this
return [...new Set(this.#args.flatMap(arg => arg.usedVariables()))];
}
// ==== Methods that are fully implemented (no need to override) ====
// Brute-force way to compare whether two formulas are equivalent:
// It provides all the used variables all possible value combinations,
// and compares the outcomes.
equivalent(other) {
const usedVariables = [...new Set(this.usedVariables().concat(other.usedVariables()))];
const trueVariables = new Set;
const recur = (i) => {
if (i >= usedVariables.length) {
// All usedVariables have a value. Make the evaluation
return this.evaluate(trueVariables) === other.evaluate(trueVariables);
}
trueVariables.delete(usedVariables[i]);
if (!recur(i + 1)) return false;
trueVariables.add(usedVariables[i]);
return recur(i + 1);
};
return recur(0);
}
// Utility functions for mapping the args member
#cnfArgs() {
return this.#args.map(arg => arg.cnf());
}
#negatedArgs() {
return this.#args.map(arg => arg.not());
}
#evalArgs(trueVariables) {
return this.#args.map(arg => arg.evaluate(trueVariables));
}
#stringifyArgs() {
return this.#args.length < 2 ? this.#args.map(String) // No parentheses needed
: this.#args.map(arg => arg.#args.length > 1 ? "(" + arg + ")" : arg + "");
}
// Giving a more verbose output than toString(). For debugging.
dump(indent="") {
return [
indent + this.constructor.name + " (",
...this.#args.map(arg => arg.dump(indent + " ")),
indent + ")"
].join("\n");
}
// ==== Static members ====
// Collection of all the variables used in any formula, keyed by name
static #variables = new Map;
// Get or create a variable, avoiding different instances for the same name
static getVariable(name) {
return this.#variables.get(name)
?? this.#variables.set(name, new this.#Variable(name)).get(name);
}
// Parse a string into a Formula.
// (No error handling: assumes the syntax is valid)
static parse(str) {
const iter = str[Symbol.iterator]();
function recur(end) {
let formula;
const connectives = [];
for (const ch of iter) {
if (ch === end) break;
if ("^v~→".includes(ch)) {
connectives.push(ch);
} else {
let arg = ch == "(" ? recur(")")
: Formula.getVariable(ch);
while (connectives.length) {
const oper = connectives.pop();
arg = oper == "~" ? arg.not()
: oper == "^" ? formula.and(arg)
: oper == "v" ? formula.or(arg)
: formula.implies(arg);
}
formula = arg;
}
}
return formula;
}
return recur();
}
// Subclasses: private.
// There is no need to create instances explicitly
// from outside the class.
static #Variable = class extends Formula {
#name;
constructor(name) {
super();
this.#name = name;
}
evaluate(trueVariables) {
return trueVariables.has(this);
}
toString() {
return this.#name;
}
dump(indent="") {
return indent + this.constructor.name + " " + this;
}
isCnf(level=0) {
return level >= 2;
}
cnf() {
return new Formula.#Conjunction(new Formula.#Disjunction(this));
}
usedVariables() {
return [this];
}
}
static #Negation = class extends Formula {
static symbol = "~";
evaluate(trueVariables) {
return !this.#evalArgs(trueVariables)[0];
}
toString() {
return this.constructor.symbol + (this.#args[0].#args.length > 1 ? `(${this.#args[0]})` : this.#args[0]);
}
isCnf(level=0) {
return level == 2 && this.#args[0].isCnf(3);
}
cnf() {
// If this is a negation of a variable, do as if it is a variable
return this.isCnf(2) ? this.#args[0].cnf.call(this)
// Else, sift down the NOT connective
: this.#args[0].negatedCnf();
}
negatedCnf() {
return this.#args[0].cnf();
}
}
static #Disjunction = class extends Formula {
static symbol = "v";
evaluate(trueVariables) {
return this.#evalArgs(trueVariables).some(Boolean);
}
isCnf(level=0) {
return level == 1 && this.#args.every(leaf => leaf.isCnf(2));
}
cnf() {
function* cartesian(firstCnf, ...otherCnfs) {
if (!firstCnf) {
yield [];
return;
}
for (const disj of firstCnf.#args) {
for (const combinations of cartesian(...otherCnfs)) {
yield [...disj.#args, ...combinations];
}
}
}
return new Formula.#Conjunction(...Array.from(
cartesian(...this.#cnfArgs()),
leaves => new Formula.#Disjunction(...leaves)
));
}
negatedCnf() {
return new Formula.#Conjunction(...this.#negatedArgs()).cnf();
}
}
static #Conjunction = class extends Formula {
static symbol = "^";
evaluate(trueVariables) {
return this.#evalArgs(trueVariables).every(Boolean);
}
isCnf(level=0) {
return level === 0 && this.#args.every(disj => disj.isCnf(1));
}
cnf() {
return this.isCnf(0) ? this // already in CNF format
: new Formula.#Conjunction(...this.#cnfArgs().flatMap(conj => conj.#args));
}
negatedCnf() {
return new Formula.#Disjunction(...this.#negatedArgs()).cnf();
}
}
static #Conditional = class extends Formula {
static symbol = "→";
evaluate(trueVariables) {
return this.#evalArgs(trueVariables).reduce((a, b) => a <= b);
}
cnf() {
return this.#args[0].not().or(this.#args[1]).cnf();
}
negatedCnf() {
return this.#args[0].and(this.#args[1].not()).cnf();
}
}
}
// Examples
// Create variables
const P = Formula.getVariable("P");
const Q = Formula.getVariable("Q");
const R = Formula.getVariable("R");
const S = Formula.getVariable("S");
const T = Formula.getVariable("T");
// Build a formula using the variables
// (P^Q^~R)v(~S^T)
const formula = P.and(Q).and(R.not()).or(S.not().and(T));
// ...or parse a string (This will create variables where needed)
const formula2 = Formula.parse("(P^Q^~R)v(~S^T)");
// Get the string representation of a formula
console.log("Formula: " + formula);
// Check whether the formula has a CNF structure
console.log("Is it CNF: " + formula.isCnf());
// Create a CNF equivalent for a formula
const cnfFormula = formula.cnf();
console.log("In CNF: " + cnfFormula);
// Verify that they are equivalent
console.log("Is equivalent? ", formula.equivalent(cnfFormula));
// Evaluate the formula providing it the set of variables that are true
console.log("When only P and T are true, this evaluates to: ", formula.evaluate(new Set([P,T])));
Alternative implementation with base and head
The rules you have listed are valid when expressions have nested con/disjunctions. Due to the recursive nature of some of the rules (where the operands are first converted to CNF, and then the top-level connective is converted), the tree's height will gradually be trimmed down as the recursion tracks back.
You indicated in comments you prefer:
Plain JSON style objects
Less language specifics
Plain functions
Keep base/head pair
So here is tested code that takes these wishes into account:
const VARIABLE = "variable";
const NEGATION = "negation";
const DISJUNCTION = "disjunction";
const CONJUNCTION = "conjunction";
const CONDITION = "condition";
// Factory functions
const variable = name => ({ type: VARIABLE, name });
const not = formula => ({ type: NEGATION, formula });
const connective = (type, base, head) => ({ type, base, head });
const or = connective.bind(null, DISJUNCTION);
const and = connective.bind(null, CONJUNCTION);
const implies = connective.bind(null, CONDITION);
// CNF related functions
function isCnf(formula) {
return isCnfChild(formula)
|| formula.type == CONJUNCTION
&& (isCnf(formula.base) || isCnfChild(formula.base))
&& (isCnf(formula.head) || isCnfChild(formula.head));
}
function isCnfChild(formula) {
return isCnfLeaf(formula)
|| formula.type == DISJUNCTION
&& (isCnfChild(formula.base) || isCnfLeaf(formula.base))
&& (isCnfChild(formula.head) || isCnfLeaf(formula.head));
}
function isCnfLeaf(formula) {
return formula.type == VARIABLE
|| (formula.type == NEGATION && formula.formula.type == VARIABLE);
}
function cnf(formula) {
if (isCnf(formula)) {
return formula;
}
switch (formula.type) {
case NEGATION:
return negatedCnf(formula.formula);
case CONJUNCTION:
return and(cnf(formula.base), cnf(formula.head));
case DISJUNCTION:
let base = cnf(formula.base);
let head = cnf(formula.head);
return base.type != CONJUNCTION
? (head.type != CONJUNCTION
? or(base, head)
: cnf(and(
or(base, head.base),
or(base, head.head)
))
)
: (head.type != CONJUNCTION
? cnf(and(
or(base.base, head),
or(base.head, head)
))
: cnf(and(
or(base.base, head.base),
and(
or(base.base, head.head),
and(
or(base.head, head.base),
or(base.head, head.head)
)
)
))
);
case CONDITION:
return cnf(or(not(formula.base), formula.head));
}
}
function negatedCnf(formula) {
switch (formula.type) {
case NEGATION:
return cnf(formula.formula);
case DISJUNCTION:
return cnf(and(not(formula.base), not(formula.head)));
case CONJUNCTION:
return cnf(or(not(formula.base), not(formula.head)));
case CONDITION:
return cnf(and(formula.base, not(formula.head)));
}
}
// Evaluation related functions
function usedVariables(formula, collect={}) {
while (formula.type == NEGATION) {
formula = formula.formula;
}
if (formula.type == VARIABLE) {
collect[formula.name] = false;
} else {
usedVariables(formula.base, collect);
usedVariables(formula.head, collect);
}
return Object.keys(collect);
}
function evaluate(formula, trueVariables) {
switch (formula.type) {
case VARIABLE:
return trueVariables.includes(formula.name);
case NEGATION:
return !evaluate(formula.formula, trueVariables);
case CONJUNCTION:
return evaluate(formula.base, trueVariables) && evaluate(formula.head, trueVariables);
case DISJUNCTION:
return evaluate(formula.base, trueVariables) || evaluate(formula.head, trueVariables);
case CONDITION:
return evaluate(formula.base, trueVariables) <= evaluate(formula.head, trueVariables);
}
}
function isEquivalent(a, b) {
const variableNames = usedVariables(and(a, b));
let trueVariables = [];
const recur = (i) => {
if (i >= variableNames.length) {
// All trueVariables have a value. Make the evaluation
return evaluate(a, trueVariables) === evaluate(b, trueVariables);
}
trueVariables.push(variableNames[i]);
if (!recur(i + 1)) return false;
trueVariables.pop();
return recur(i + 1);
};
return recur(0);
}
// String conversion functions
function bracket(formula) {
if ([VARIABLE, NEGATION].includes(formula.type)) {
return stringify(formula);
}
return "(" + stringify(formula) + ")";
}
function stringify(formula) {
switch (formula.type) {
case VARIABLE:
return formula.name;
case NEGATION:
return "~" + bracket(formula.formula);
case CONJUNCTION:
return bracket(formula.base) + "^" + bracket(formula.head);
case DISJUNCTION:
return bracket(formula.base) + "v" + bracket(formula.head);
case CONDITION:
return bracket(formula.base) + "→" + bracket(formula.head);
}
}
function parse(str) {
const iter = str[Symbol.iterator]();
function recur(end) {
let formula;
const connectives = [];
for (const ch of iter) {
if (ch === end) break;
if ("^v~→".includes(ch)) {
connectives.push(ch);
} else {
let arg = ch == "(" ? recur(")")
: variable(ch);
while (connectives.length) {
const oper = connectives.pop();
arg = oper == "~" ? not(arg)
: oper == "^" ? and(formula, arg)
: oper == "v" ? or(formula, arg)
: implies(formula, arg);
}
formula = arg;
}
}
return formula;
}
return recur();
}
function demo() {
// Create variables
const P = variable("P");
const Q = variable("Q");
const R = variable("R");
const S = variable("S");
const T = variable("T");
// Build a formula using the variables
// (P^Q^~R)v(~S^T)
const formula = or(and(and(P, Q), not(R)), and(not(S), T));
// ...or parse a string (This will create variables where needed)
const formula2 = parse("(P^Q^~R)v(~S^T)");
// Get the string representation of a formula
console.log("Formula: " + stringify(formula));
// Check whether the formula has a CNF structure
console.log("Is it CNF: " + isCnf(formula));
// Create a CNF equivalent for a formula
const cnfFormula = cnf(formula);
console.log("In CNF: " + stringify(cnfFormula));
// Verify that they are equivalent
console.log("Is equivalent? ", isEquivalent(formula, cnfFormula));
// Evaluate the formula providing it the set of variables that are true
console.log("When only P and T are true, this evaluates to: ", evaluate(formula, [P,T]));
}
demo();
Here's an approach that tries to follow the algorithm described in that question, and it's fairly close to what was described on Wikipedia.
It doesn't precisely answer your question, as we start with an entirely different model. I hope it might serve as a guide for working with your approach.
We create some factory functions that could be used like this:
Not (Or (Var ('A'), Var ('B')))
to us to build
{
type: "Not",
vals: [
{
type: "Or",
vals: [
{
type: "Var",
vals: [
"A"
]
},
{
type: "Var",
vals: [
"B"
]
}
]
}
]
}
and two different functions to turn that into a String:
stringify yields "not (or (A, B))"
display yields "~(A v B)"
Note that the formulae all have the same {type, vals} structure, even though Not and Var have only one child and Implies and Equiv each have exactly two. And and Or can have as many as needed, but the factory does try to imply that you will build this with exactly two.
This is not a head/tail list. We could convert it to one with only slight difficulty, but often arrays are easier to work with in JS. Here at one point we slice the array to get an initial portion and again to get a final portion. One of those would likely be trickier with pair-based lists. (This is not at all impossible, but I find this approach simpler.)
We then have a three formula transformation functions:
negNormForm transforms nested Nots and Nots applied to conjunctions and disjunctions and transforms Implies and Equiv to appropriate Or/And/Not nested structure.
disjOverConj distributes (P v (Q ^ R)) to make ((P v Q) ^ (P v R)).
flattenConjDisj turns, for instance ((A v B) v C) into (A v B v C), and similarly for ^
The main function, cnf just composes these three transformations.
The code looks like this:
const Formula = {
Var: (v) => ({type: 'Var', vals: [v]}),
And: (p, q, ...ps) => ({type: 'And', vals: [p, q, ...ps]}),
Or: (p, q, ...ps) => ({type: 'Or', vals: [p, q, ...ps]}),
Not: (p) => ({type: 'Not', vals: [p]}),
Implies: (p, q) => ({type: 'Implies', vals: [p, q]}),
Equiv: (p, q) => ({type: 'Equiv', vals: [p, q]}),
}
const {Var, And, Or, Not, Implies, Equiv} = Formula
const run = (xs) => (formula) => {
const fn = xs [formula .type]
return fn ? fn (formula .vals) : formula
}
const postorder = (fn) => (f) =>
f .type == 'Var'
? fn (f)
: fn (Formula [f .type] (... f .vals .map (postorder (fn))))
const stringify = run ({
Var: ([val]) => String (val),
And: (vals) => `and (${vals .map (stringify) .join (', ')})`,
Or: (vals) => `or (${vals .map (stringify) .join (', ')})`,
Not: ([val]) => `not (${stringify (val)})`,
Implies: ([p, q]) => `implies (${stringify (p)}, ${stringify (q)})`,
Equiv: ([p, q]) => `equiv (${stringify (p)}, ${stringify (q)})`,
})
const display = run ({
Var: ([val]) => String (val),
And: (vals) => `(${vals .map (display) .join (' ^ ')})`, // or ∧
Or: (vals) => `(${vals .map (display) .join (' v ')})`, // or ∨
Not: ([val]) => `~${display (val)}`, // or ¬
Implies: ([p, q]) => `(${display (p)} => ${display (q)})`, // or →
Equiv: ([p, q]) => `(${display (p)} <=> ${display (q)})`, // or ↔
})
const flattenConjDisj = postorder (run ({
And: (vals) => And (... vals .flatMap ((f) => f.type == 'And' ? f .vals : [f])),
Or: (vals) => Or (... vals .flatMap ((f) => f.type == 'Or' ? f .vals : [f])),
}))
const negNorm = postorder (run ({
Not: ([val]) => // --\
val .type == 'Not' // +--- Double-negative
? negNorm (val .vals [0]) // --/
: val .type == 'And' // --\
? Or (... val .vals .map ((v) => Not (v))) // +--- DeMorgan's Laws
: val .type == 'Or' // |
? And (... val .vals .map ((v) => Not (v))) // --/
: Not (val),
Implies: ([p, q]) => Or (Not (p), q),
Equiv: ([p, q]) => And (Or (p, Not (q)), Or (Not (p), q))
}))
const conjOverDisj = postorder (run ({
Or: (vals, andIdx = vals .findIndex ((x) => x .type == 'And'), and = vals [andIdx]) =>
andIdx > -1 //--\
? And (... and .vals .flatMap ( // +--- Distribution
(v) => conjOverDisj ( Or (... vals .slice (0, andIdx), v, ...vals .slice (andIdx + 1)))
)) //--/
: Or (...vals),
}))
const cnf = (f) => flattenConjDisj (conjOverDisj (negNorm (f)))
const testCases = [
Or (Var ('P'), And (Var ('Q'), Var ('R'))),
Not (Or (Var ('A'), Var ('B'))),
And (Not (Not (Not (Var ('A')))), Equiv (Var ('B'), Var ('C')),),
And (Var ('A'), And (Var ('B'), And (Implies (Var ('C'), Var ('D')), Not (Not (Not (Var ('E'))))))),
Equiv (Var ('P'), Var ('Q')),
Or (And (Var ('A'), Var ('B')), Var ('C'), And (Var ('D'), Var ('E'))),
]
console .log ('----------------------------------------------')
testCases .forEach ((x) => {
console .log ('Original: ')
console .log (' ' + stringify (x))
console .log (' ' + display (x))
console .log ('Conjunctive Normal Form:')
console .log (' ' + stringify (cnf (x)))
console .log (' ' + display (cnf (x)))
console .log ('----------------------------------------------')
})
.as-console-wrapper {max-height: 100% !important; top: 0}
After the factory functions, we have two helpers. postorder walks the tree in a postorder fashion, running a transformation function on each node. run is a small helper, which is best seen by example:
// v--------- right here
const stringify = run ({
Var: ([val]) => String (val),
And: (vals) => `and (${vals .map (stringify) .join (', ')})`,
Or: (vals) => `or (${vals .map (stringify) .join (', ')})`,
Not: ([val]) => `not (${stringify (val)})`,
Implies: ([p, q]) => `implies (${stringify (p)}, ${stringify (q)})`,
Equiv: ([p, q]) => `equiv (${stringify (p)}, ${stringify (q)})`,
})
The result of this call, stringify is a function which takes a formula and passes its vals to the appropriate function based on its type. The appropriate formulas are supplied in the configuration object passed to run. This function simplifies the implementations of the other major function here, so it seems worth having, but I can't come up with a good name for it, and that makes me worry that it's not an appropriate abstraction. Still, it works for now. An earlier version looked like this:
const run = (xs) => ({type, vals}) => xs [type] (vals)
That works fine, but it requires the configuration object to handle every type and not just the ones it's interested in. (This is more important for the structure transformations function than for the display and stringify, which have to handle them all anyway.)
The other three main functions, flattenConjDisj, negNorm, and disjOverConj combine run with postorder to transform a tree based on individual type-specific functions.
I think the code for the first two is fairly clear. Please add a comment if it's not. The third one is trickier. We are distributing our And over our Or. Because our Or formulae can have more than two children, we do this recursively until none of the children is an And. When we find an And, we take all its children and combine them with all their peers. So to process this:
((A ^ B) v C v (D ^ E))
We note the first And and expand that, turning this into
((A v C) ^ (B v C)) v (D ^ E))
And then the recursive all expands the second And, yielding
(((A v C v D) ^ (A v C v E)) ^ ((B v C v D) ^ (B v C v E)))
When we call the flattening, that will turn this into
((A v C v D) ^ (A v C v E) ^ (B v C v D) ^ (B v C v E))
Again, if that's not clear, please ask for clarification in the comments.
A fun problem!
Note: I realized as I was typing this up that I didn't handle xor. At this point (approaching bedtime), I leave that as an exercise for the reader. It should be straightforward.
I think your tree data structure supports any kinds of nested formula just fine. See the example below where I define a test case of ((A ^ B) V (C ^ D)) ^ E ^ F.
test = {
type: 'conjunction',
base: {
type: 'disjunction',
base: {
type: 'conjunction',
base: { type: 'variable', name: 'A'},
head: { type: 'variable', name: 'B'}
},
head: {
type: 'conjunction',
base: { type: 'variable', name: 'C'},
head: { type: 'variable', name: 'D'},
}
},
head : {
type: 'conjunction',
base: { type: 'variable', name: 'E'},
head: { type: 'variable', name: 'F'},
}
}
What you need is a way to check if a formula is a disjunction of literals (I call it DoL in code for short). Also useful to have a way to check if a formula is CNF.
function isDisjunctionOfLiteral(formula) {
if (
(formula.type === 'variable' ) ||
(
formula.type === 'disjunction' &&
isDisjunctionOfLiteral(formula.base) &&
isDisjunctionOfLiteral(formula.head)
)
) {return true}
else {return false}
}
function isCNF(formula) {
if (
isDisjunctionOfLiteral(formula) ||
(
formula.type === 'conjunction' &&
isCNF(formula.base) &&
isCNF(formula.head)
)
) {return true}
else {return false}
}
Now we are ready to implement Conjunction and Disjunction cases (I leave the other 4 cases).
/*
* Any syntactically valid propositional formula φ must fall into
* exactly one of the following 7 cases (that is, it is an instanceof
* one of the 7 subclasses of Formula).
*
* #see https://www.cs.jhu.edu/~jason/tutorials/convert-to-CNF.html
*/
function convert(formula) {
switch (formula.type) {
case 'variable': return formula
case 'conjunction': return convertConjunction(formula)
case 'disjunction': return convertDisjunction(formula)
/*
case 'negation': return convertNegation(formula)
case 'conditional': return convertConditional(formula)
case 'biconditional': return convertBiconditional(formula)
case 'xor': return convertXOR(formula)
*/
default:
throw new Error(`Unknown formula type ${formula.type}.`)
}
}
function convertConjunction(formula) {
// CONVERT(P) must have the form P1 ^ P2 ^ ... ^ Pm, and
let cnfBase = convert(formula.base);
// CONVERT(Q) must have the form Q1 ^ Q2 ^ ... ^ Qn,
let cnfHead = convert(formula.head);
// where all the Pi and Qi are disjunctions of literals.
// So return P1 ^ P2 ^ ... ^ Pm ^ Q1 ^ Q2 ^ ... ^ Qn.
return {
type: 'conjunction',
base: cnfBase ,
head: cnfHead,
}
}
function convertDisjunction(formula) {
// CONVERT(P) must have the form P1 ^ P2 ^ ... ^ Pm, and
let cnfBase = convert(formula.base);
// CONVERT(Q) must have the form Q1 ^ Q2 ^ ... ^ Qn,
let cnfHead = convert(formula.head);
// where all the Pi and Qi are dijunctions of literals.
// So we need a CNF formula equivalent to
// (P1 ^ P2 ^ ... ^ Pm) v (Q1 ^ Q2 ^ ... ^ Qn).
// So return (P1 v Q1) ^ (P1 v Q2) ^ ... ^ (P1 v Qn)
// ^ (P2 v Q1) ^ (P2 v Q2) ^ ... ^ (P2 v Qn)
// ...
// ^ (Pm v Q1) ^ (Pm v Q2) ^ ... ^ (Pm v Qn)
let finalResult = {type: 'conjunction'}; // finalResult is a tree of conjunctions with m x n elements
let result = finalResult; // pointer to each element in finalResult
let prevResult;
function addEntry(item) {
result.base = item;
result.head = {type: 'conjunction'};
prevResult = result;
result = result.head;
}
forEachDoL(cnfBase, (baseDoL) => {
forEachDoL(cnfHead, (headDoL) => {
addEntry({
type: 'disjunction',
base: baseDoL,
head: headDoL
});
});
});
// finally, fix the last node of the tree
// prevResult = prevResult.base;
let prevBase = prevResult.base
prevResult.type = prevBase.type;
prevResult.base = prevBase.base;
prevResult.head = prevBase.head;
return finalResult;
}
function forEachDoL(cnf, callback) {
if (!isCNF(cnf)) throw new Error('argument is not CNF');
if (isDisjunctionOfLiteral(cnf)) {
callback(cnf)
} else {
forEachDoL(cnf.base, callback);
forEachDoL(cnf.head, callback);
}
}
Finally, included a print function to visualize our test case. It successfully converts ((A ^ B) V (C ^ D)) ^ E ^ F to (A V C) ^ (A V D) ^ (B V C) ^ (B V D) ^ E ^ F.
function printValues(obj, level) {
level = level || 0;
for (var key in obj) {
if (typeof obj[key] === "object") {
console.log(" ".repeat(level*2) + key + " : ");
printValues(obj[key], level + 1);
} else {
console.log(" ".repeat(level*2) + key + " : " + obj[key]);
}
}
}
printValues(convert(test));

Ramda apply an argument to both functions and compose them point free

I have two curried functions f and g:
f: a -> b -> c
g: a -> c -> d
I want to create h:
h: a -> b -> d
Currently I'm composing them via pipe:
const h = a => pipe(f(a), g(a));
Is there a way to do this point free?
The reason why I ask is because I want to make the code as readable as possible. In some of my cases there are more that two functions that receive initial arguments in the composition and I'm trying to avoid repeatedly passing arguments.
I tried something like:
const h = converge(pipe, [f, g]);
which for some reason doesn't work. I guess I don't understand converge correctly.
Edit:
I tried what you suggested Ori Drori, but it doesn't work.
const modulo = divisor => dividend => dividend % divisor;
const maybeReduceByModulo = modulo => number =>
number >= 0 ? number : number + modulo;
export const maybeReducedModulo = flip(ap)(modulo, maybeReduceByModulo);
/**
* Before:
* export const maybeReducedModulo = divisor =>
* pipe(modulo(divisor), maybeReduceByModulo(divisor));
*/
Ramda (disclaimer: I'm a Ramda author) cannot directly offer every combinator one might need. It does contain a number of them. But for those it doesn't contain, it's often trivial to write your own version. For me the big problem is in naming. It's hard to come up with names for them, and even those who resort to bird names can only give names for a few of the infinity of possibilities.
I can't find this one in Avaq's handy list of combinators, nor can I find :: (a -> b -> c) -> (a -> c -> d) -> a -> b -> d or :: (a -> b -> c) -> (a -> c -> d) -> (a -> b -> d) on Hoogle, which makes me suspect that this is a fairly uncommon requirement. But if it's a requirement you have, invent your own name.
Once you've chosen a name, such combinators often write themselves.
const foo = (f) => (g) => (x) => (y) => g (x) (f (x) (y))
const f = (a) => (b) => `f (${a}, ${b})`
const g = (a) => (c) => `g (${a}, ${c})`
const h = foo (f) (g)
console .log (h ('a') ('b'))
And of course you can play with the signatures in various ways. Ramda's curry might help here. Perhaps we want this:
const foo = (f, g) => (x) => (y) => g (x) (f (x) (y))
const h = foo (f, g)
or even further, this:
const foo = curry ((f, g) => curry ((x, y) => g (x) (f (x) (y))))
const h = foo (f, g) // equivalent: `foo (f) (g)`
h ('a', 'b') // equivalent: `h ('a') ('b')`
The suggestion from customcommander does work:
const f = (a) => (b) => `f (${a}, ${b})`
const g = (a) => (c) => `g (${a}, ${c})`
const h = compose (apply (pipe), ap ([f, g]), of)
console .log (h ('a') ('b'))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script> const {compose, apply, pipe, ap, of} = R </script>
But I think it's not nearly as understandable as the more explicit vanilla JS version.

How to conditionally assign to a variable in functional way?

I need to assign a value to a variable based on condition. I want to do it with a functional programming paradigm in mind, thus I cannot declare it in outer scope and then reassign.
// let foo = undefined // no-let!
if(condition) {
const foo = 1
} else {
const foo = 0
}
do_some_stuff(foo) // Uncaught ReferenceError: foo is not defined
I know, I can use ternary expression here like
const foo = condition ? 1 : 0
but what if I have some other routine to do inside my condition, like:
if(condition) {
const foo = 1
do_stuff()
} else {
const foo = 0
do_other_stuff()
}
do_third_stuff(foo)
Probably you would encode your case using some algebraic data type (ADT) like Either. That is, you may cover two subcases: left and right.
See the code from // --> Solution starts here onwards. Previous code is a mini standard FP library using vanilla JavaScript to make the code runnable. Check it and enjoy!
// Mini standard library
// -------------------------------
// The identity combinator
// I :: a -> a
const I = x => x
// Either ADT
const Either = (() => {
// Creates an instance of Either.Right
//
// of :: b -> Either a b
const of = x => ({ right: x })
// Creates an instance of Either.Right
//
// Right :: b -> Either a b
const Right = of
// Creates an instance of Either.Left
//
// Left :: a -> Either a b
const Left = x => ({ left: x })
// Maps Either.Left or Either.Right in a single operation
//
// bimap :: (a -> c) -> (b -> d) -> Either a b -> Either c -> d
const bimap = f => g => ({ left, right }) => left ? Left (f (left)) : Right (g (right))
// Lifts a value to Either based on a condition, where false
// results in Left, and true is Right.
//
// tagBy :: (a -> Boolean) -> a -> Either a a
const tagBy = f => x => f (x) ? Right (x) : Left (x)
// Unwraps Either.Left or Either.Right with mapping functions
//
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = f => g => ({ left, right }) => left ? f (left) : g (right)
// Unwraps Either.Left or Either.Right and outputs the raw value on them
//
// unwrap :: Either a b -> c
const unwrap = either (I) (I)
return { of, Right, Left, bimap, tagBy, either, unwrap }
}) ()
// --> Solution starts here
// Lifts to Either.Right if x is greater than 3,
// otherwise, x is encoded as Left.
//
// tagGt3 :: Number -> Either Number Number
const tagGt3 = Either.tagBy (x => x > 3)
// doStuff :: Number -> Number
const doStuff = x => x + 1
// doStuff2 :: Number -> Number
const doStuff2 = x => x * 4
// doStuff3 :: Either Number Number -> Either Number Number
const doStuff3 = Either.bimap (doStuff) (doStuff2) // <-- here's the decision!
const eitherValue1 = doStuff3 (tagGt3 (2))
const eitherValue2 = doStuff3 (tagGt3 (30))
const output1 = Either.unwrap (eitherValue1)
const output2 = Either.unwrap (eitherValue2)
console.log ('output1: ', output1)
console.log ('output2: ', output2)
Refactor using pipes
Now I introduce pipe to glue a composition of one or more unary functions, which makes the code more elegant:
// Mini standard library
// -------------------------------
// The identity combinator
// I :: a -> a
const I = x => x
// Pipes many unary functions
//
// pipe :: [a -> b] -> a -> c
const pipe = xs => x => xs.reduce ((o, f) => f (o), x)
// Either ADT
const Either = (() => {
// Creates an instance of Either.Right
//
// of :: b -> Either a b
const of = x => ({ right: x })
// Creates an instance of Either.Right
//
// Right :: b -> Either a b
const Right = of
// Creates an instance of Either.Left
//
// Left :: a -> Either a b
const Left = x => ({ left: x })
// Maps Either.Left or Either.Right in a single operation
//
// bimap :: (a -> c) -> (b -> d) -> Either a b -> Either c -> d
const bimap = f => g => ({ left, right }) => left ? Left (f (left)) : Right (g (right))
// Lifts a value to Either based on a condition, where false
// results in Left, and true is Right.
//
// tagBy :: (a -> Boolean) -> a -> Either a a
const tagBy = f => x => f (x) ? Right (x) : Left (x)
// Unwraps Either.Left or Either.Right with mapping functions
//
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = f => g => ({ left, right }) => left ? f (left) : g (right)
// Unwraps Either.Left or Either.Right and outputs the raw value on them
//
// unwrap :: Either a b -> c
const unwrap = either (I) (I)
return { of, Right, Left, bimap, tagBy, either, unwrap }
}) ()
// --> Solution starts here
// doStuff :: Number -> Number
const doStuff = x => x + 1
// doStuff2 :: Number -> Number
const doStuff2 = x => x * 4
const { tagBy, bimap, unwrap } = Either
// doStuff3 :: Number -> Number
const doStuff3 = pipe ([
tagBy (x => x > 3),
bimap (doStuff) (doStuff2), // <-- here's the decision!
unwrap
])
const output1 = doStuff3 (2)
const output2 = doStuff3 (30)
console.log ('output1: ', output1)
console.log ('output2: ', output2)
Nothing is stopping you from splitting the two:
const foo = condition ? 1 : 0;
if(condition) {
do_stuff();
} else {
do_other_stuff();
}
do_third_stuff(foo);
In case condition is an expensive execution, simply assign it to a variable before using it multiple times:
let isFoo = expensiveIsFooMethod();
const foo = isFoo ? 1 : 0;
if(isFoo) {
do_stuff();
} else {
do_other_stuff();
}
do_third_stuff(foo);
You're right that it would be cleaner if you didn't have to repeat the condition, but you've introduced this limitation because you're using a const variable which makes it impossible to assign a value to your const in more than one place.
I suggest outweighing the two options here. What matters to you more: cleaner syntax, or ensuring you'll never overwrite the value?
Because you dont want to declare foo outside, why you do not simply this way:
if(condition) {
const foo = 1
do_stuff()
do_third_stuff(foo)
} else {
const foo = 0
do_other_stuff()
do_third_stuff(foo)
}

Function that will execute function depending on value. Functional programming

I have two functions and they are executed depending of if statement. E.g.:
if(value) {
doA()
} else {
doB()
}
How to write function or object that will take the result and decide whether or not execute each function. I want to receive something like this:
exists(result).doA()
nothing(result).doB()
I want to learn some functional programming in JavaScrit so I woud appreciate any source from which I can learn FP in JavaScript.
continuation passing style
here's an approach using continuation passing style. you'll notice the implementation of main is not far off from your original encoding –
once you finish wrapping your head around this, if you haven't already learned about monads, you now know the best one (cont) ^_^
// cont :: a -> (a -> b) -> b
const cont = x =>
k => k (x)
// when :: (a -> boolean, a -> b, a -> b) -> a -> (a -> b) -> b
const when = (f,l,r) => x =>
f (x) ? cont (l (x)) : cont (r (x))
// isOdd :: number -> boolean
const isOdd = x =>
x & 1 === 1
// doA :: number -> number
const doA = x =>
x + 1
// doB :: number -> number
const doB = x =>
x * x
// main :: number -> void
const main = x =>
cont (x) (when (isOdd, doA, doB)) (console.log)
main (3) // IO: 4, doA (3) === 3 + 1
main (4) // IO: 16, doB (4) === 4 * 4
data constructors
here's another approach using simple data constructors Left and Right to represent a Fork sum type – this results in a sort of data-directed style where the control of main is determined by the input type
// type Fork a = Left a | Right a
// Left a :: { fork :: (a -> b, _) -> b }
const Left = x =>
({ type: Left, fork: (f,_) => f (x) })
// Right a :: { fork :: (_, a -> b) -> b }
const Right = x =>
({ type: Right, fork: (_,f) => f (x) })
// doA :: number -> number
const doA = x =>
x + 1
// doB :: number -> number
const doB = x =>
x * x
// main :: Fork a -> a
const main = f =>
// fork applies the left function (doA) to a Left
// fork applies the right function (doB) to a Right
f.fork (doA, doB)
console.log (main (Left (3))) // => 4, doA (3) === 3 + 1
console.log (main (Right (3))) // => 9, doB (3) === 3 * 3
You could write something like this, for example:
function exists(value) {
return function (func) {
if (value !== undefined) {
return func(value);
}
return null;
}
}
function nothing(value) {
return function (func) {
if (value === undefined) {
return func();
}
return null;
}
}
function doA(value) {
console.log('doing A', value);
}
function doB() {
console.log('doing B');
}
const foo = 'fool';
const bar = undefined;
exists(foo)(doA);
nothing(bar)(doB);
The exists function gets a value and returns another function. The function that is returned gets yet another function as argument, which is executed if the value passed is defined.
I've used “old school” anonymous functions in the example above to make it easier to understand. With ES6 arrow functions, you can write the exists and nothing functions more consisely, like this:
function exists(value) {
return func => value !== undefined ? func(value) : null;
}
function nothing(value) {
return func => value === undefined ? func(value) : null;
}
The “functional programming fun” really starts when you realize you can refactor these two functions by putting the common code in another function, that is then used to create the two functions, like this:
function executeWithCondition(predicate) {
return value => func => predicate(value) ? func(value) : null;
}
const exists = executeWithCondition(value => value !== undefined);
const nothing = executeWithCondition(value => value === undefined);
This technique is called currying.
Usage of these functions is still the same:
exists(foo)(doA);
nothing(bar)(doB);
Here's the complete runnable code example:
function executeWithCondition(predicate) {
return value => func => predicate(value) ? func(value) : null;
}
const exists = executeWithCondition(value => value !== undefined);
const nothing = executeWithCondition(value => value === undefined);
function doA(value) {
console.log('doing A', value);
}
function doB() {
console.log('doing B');
}
const foo = 'fool';
const bar = undefined;
exists(foo)(doA);
nothing(bar)(doB);
One approach would be to define the properties of an object with values set to functions
const o = {
exists: function(value) {
return value ? this.doA() : this.doB()
},
doA: function(value) {
console.log("A")
},
doB: function(value) {
console.log("B")
}
}
o.exists(void 0);

javascript: recursive anonymous function?

Let's say I have a basic recursive function:
function recur(data) {
data = data+1;
var nothing = function() {
recur(data);
}
nothing();
}
How could I do this if I have an anonymous function such as...
(function(data){
data = data+1;
var nothing = function() {
//Something here that calls the function?
}
nothing();
})();
I'd like a way to call the function that called this function... I've seen scripts somewhere (I can't remember where) that can tell you the name of a function called, but I can't recall any of that information right now.
You can give the function a name, even when you're creating the function as a value and not a "function declaration" statement. In other words:
(function foo() { foo(); })();
is a stack-blowing recursive function. Now, that said, you probably don't may not want to do this in general because there are some weird problems with various implementations of Javascript. (note — that's a fairly old comment; some/many/all of the problems described in Kangax's blog post may be fixed in more modern browsers.)
When you give a name like that, the name is not visible outside the function (well, it's not supposed to be; that's one of the weirdnesses). It's like "letrec" in Lisp.
As for arguments.callee, that's disallowed in "strict" mode and generally is considered a bad thing, because it makes some optimizations hard. It's also much slower than one might expect.
edit — If you want to have the effect of an "anonymous" function that can call itself, you can do something like this (assuming you're passing the function as a callback or something like that):
asyncThingWithCallback(params, (function() {
function recursive() {
if (timeToStop())
return whatever();
recursive(moreWork);
}
return recursive;
})());
What that does is define a function with a nice, safe, not-broken-in-IE function declaration statement, creating a local function whose name will not pollute the global namespace. The wrapper (truly anonymous) function just returns that local function.
People talked about the Y combinator in comments, but no one wrote it as an answer.
The Y combinator can be defined in javascript as follows: (thanks to steamer25 for the link)
var Y = function (gen) {
return (function(f) {
return f(f);
}(function(f) {
return gen(function() {
return f(f).apply(null, arguments);
});
}));
}
And when you want to pass your anonymous function:
(Y(function(recur) {
return function(data) {
data = data+1;
var nothing = function() {
recur(data);
}
nothing();
}
})());
The most important thing to note about this solution is that you shouldn't use it.
U combinator
By passing a function to itself as an argument, a function can recur using its parameter instead of its name! So the function given to U should have at least one parameter that will bind to the function (itself).
In the example below, we have no exit condition, so we will just loop indefinitely until a stack overflow happens
const U = f => f (f) // call function f with itself as an argument
U (f => (console.log ('stack overflow imminent!'), U (f)))
We can stop the infinite recursion using a variety of techniques. Here, I'll write our anonymous function to return another anonymous function that's waiting for an input; in this case, some number. When a number is supplied, if it is greater than 0, we will continue recurring, otherwise return 0.
const log = x => (console.log (x), x)
const U = f => f (f)
// when our function is applied to itself, we get the inner function back
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)
// returns: (x => x > 0 ? U (f) (log (x - 1)) : 0)
// where f is a reference to our outer function
// watch when we apply an argument to this function, eg 5
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0) (5)
// 4 3 2 1 0
What's not immediately apparent here is that our function, when first applied to itself using the U combinator, it returns a function waiting for the first input. If we gave a name to this, can effectively construct recursive functions using lambdas (anonymous functions)
const log = x => (console.log (x), x)
const U = f => f (f)
const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)
countDown (5)
// 4 3 2 1 0
countDown (3)
// 2 1 0
Only this isn't direct recursion – a function that calls itself using its own name. Our definition of countDown does not reference itself inside of its body and still recursion is possible
// direct recursion references itself by name
const loop = (params) => {
if (condition)
return someValue
else
// loop references itself to recur...
return loop (adjustedParams)
}
// U combinator does not need a named reference
// no reference to `countDown` inside countDown's definition
const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)
How to remove self-reference from an existing function using U combinator
Here I'll show you how to take a recursive function that uses a reference to itself and change it to a function that employs the U combinator to in place of the self reference
const factorial = x =>
x === 0 ? 1 : x * factorial (x - 1)
console.log (factorial (5)) // 120
Now using the U combinator to replace the inner reference to factorial
const U = f => f (f)
const factorial = U (f => x =>
x === 0 ? 1 : x * U (f) (x - 1))
console.log (factorial (5)) // 120
The basic replacement pattern is this. Make a mental note, we will be using a similar strategy in the next section
// self reference recursion
const foo = x => ... foo (nextX) ...
// remove self reference with U combinator
const foo = U (f => x => ... U (f) (nextX) ...)
Y combinator
related: the U and Y combinators explained using a mirror analogy
In the previous section we saw how to transform self-reference recursion into a recursive function that does not rely upon a named function using the U combinator. There's a bit of an annoyance tho with having to remember to always pass the function to itself as the first argument. Well, the Y-combinator builds upon the U-combinator and removes that tedious bit. This is a good thing because removing/reducing complexity is the primary reason we make functions
First, let's derive our very own Y-combinator
// standard definition
const Y = f => f (Y (f))
// prevent immediate infinite recursion in applicative order language (JS)
const Y = f => f (x => Y (f) (x))
// remove reference to self using U combinator
const Y = U (h => f => f (x => U (h) (f) (x)))
Now we will see how it's usage compares to the U-combinator. Notice, to recur, instead of U (f) we can simply call f ()
const U = f => f (f)
const Y = U (h => f => f (x => U (h) (f) (x)))
Y (f => (console.log ('stack overflow imminent!'), f ()))
Now I'll demonstrate the countDown program using Y – you'll see the programs are almost identical but the Y combinator keeps things a bit cleaner
const log = x => (console.log (x), x)
const U = f => f (f)
const Y = U (h => f => f (x => U (h) (f) (x)))
const countDown = Y (f => x => x > 0 ? f (log (x - 1)) : 0)
countDown (5)
// 4 3 2 1 0
countDown (3)
// 2 1 0
And now we'll see factorial as well
const U = f => f (f)
const Y = U (h => f => f (x => U (h) (f) (x)))
const factorial = Y (f => x =>
x === 0 ? 1 : x * f (x - 1))
console.log (factorial (5)) // 120
As you can see, f becomes the mechanism for recursion itself. To recur, we call it like an ordinary function. We can call it multiple times with different arguments and the result will still be correct. And since it's an ordinary function parameter, we can name it whatever we like, such as recur below -
const U = f => f (f)
const Y = U (h => f => f (x => U (h) (f) (x)))
const fibonacci = Y (recur => n =>
n < 2 ? n : recur (n - 1) + (n - 2))
console.log (fibonacci (10)) // 55
U and Y combinator with more than 1 parameter
In the examples above, we saw how we can loop and pass an argument to keep track of the "state" of our computation. But what if we need to keep track of additional state?
We could use compound data like an Array or something...
const U = f => f (f)
const Y = U (h => f => f (x => U (h) (f) (x)))
const fibonacci = Y (f => ([a, b, x]) =>
x === 0 ? a : f ([b, a + b, x - 1]))
// starting with 0 and 1, generate the 7th number in the sequence
console.log (fibonacci ([0, 1, 7]))
// 0 1 1 2 3 5 8 13
But this is bad because it's exposing internal state (counters a and b). It would be nice if we could just call fibonacci (7) to get the answer we want.
Using what we know about curried functions (sequences of unary (1-paramter) functions), we can achieve our goal easily without having to modify our definition of Y or rely upon compound data or advanced language features.
Look at the definition of fibonacci closely below. We're immediately applying 0 and 1 which are bound to a and b respectively. Now fibonacci is simply waiting for the last argument to be supplied which will be bound to x. When we recurse, we must call f (a) (b) (x) (not f (a,b,x)) because our function is in curried form.
const U = f => f (f)
const Y = U (h => f => f (x => U (h) (f) (x)))
const fibonacci = Y (f => a => b => x =>
x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)
console.log (fibonacci (7))
// 0 1 1 2 3 5 8 13
This sort of pattern can be useful for defining all sorts of functions. Below we'll see two more functions defined using the Y combinator (range and reduce) and a derivative of reduce, map.
const U = f => f (f)
const Y = U (h => f => f (x => U (h) (f) (x)))
const range = Y (f => acc => min => max =>
min > max ? acc : f ([...acc, min]) (min + 1) (max)) ([])
const reduce = Y (f => g => y => ([x,...xs]) =>
x === undefined ? y : f (g) (g (y) (x)) (xs))
const map = f =>
reduce (ys => x => [...ys, f (x)]) ([])
const add = x => y => x + y
const sq = x => x * x
console.log (range (-2) (2))
// [ -2, -1, 0, 1, 2 ]
console.log (reduce (add) (0) ([1,2,3,4]))
// 10
console.log (map (sq) ([1,2,3,4]))
// [ 1, 4, 9, 16 ]
IT'S ALL ANONYMOUS OMG
Because we're working with pure functions here, we can substitute any named function for its definition. Watch what happens when we take fibonacci and replace named functions with their expressions
/* const U = f => f (f)
*
* const Y = U (h => f => f (x => U (h) (f) (x)))
*
* const fibonacci = Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)
*
*/
/*
* given fibonacci (7)
*
* replace fibonacci with its definition
* Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
*
* replace Y with its definition
* U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
//
* replace U with its definition
* (f => f (f)) U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
*/
let result =
(f => f (f)) (h => f => f (x => h (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
console.log (result) // 13
And there you have it – fibonacci (7) calculated recursively using nothing but anonymous functions
It may be simplest to use an "anonymous object" instead:
({
do: function() {
console.log("don't run this ...");
this.do();
}
}).do();
Your global space is completely unpolluted. It's pretty straightforward. And you can easily take advantage of the object's non-global state.
You can also use ES6 object methods to make the syntax more concise.
({
do() {
console.log("don't run this ...");
this.do();
}
}).do();
I would not do this as an inline function. It's pushing against the boundaries of good taste and doesn't really get you anything.
If you really must, there is arguments.callee as in Fabrizio's answer. However this is generally considered inadvisable and is disallowed in ECMAScript Fifth Edition's ‘strict mode’. Although ECMA 3 and non-strict-mode are not going away, working in strict mode promises more possible language optimisations.
One can also use a named inline function:
(function foo(data){
data++;
var nothing = function() {
foo(data);
}
nothing();
})();
However named inline function expressions are also best avoided, as IE's JScript does some bad things to them. In the above example foo incorrectly pollutes the parent scope in IE, and the parent foo is a separate instance to the foo seen inside foo.
What's the purpose of putting this in an inline anonymous function? If you just want to avoid polluting the parent scope, you can of course hide your first example inside another self-calling-anonymous-function (namespace). Do you really need to create a new copy of nothing each time around the recursion? You might be better off with a namespace containing two simple mutually-recursive functions.
(function(data){
var recursive = arguments.callee;
data = data+1;
var nothing = function() {
recursive(data)
}
nothing();
})();
You could do something like:
(foo = function() { foo(); })()
or in your case:
(recur = function(data){
data = data+1;
var nothing = function() {
if (data > 100) return; // put recursion limit
recur(data);
}
nothing();
})(/* put data init value here */ 0);
When you declare an anonymous function like this:
(function () {
// Pass
}());
Its considered a function expression and it has an optional name (that you can use to call it from within itself. But because it's a function expression (and not a statement) it stays anonymous (but has a name that you can call). So this function can call itself:
(function foo () {
foo();
}());
foo //-> undefined
Why not pass the function to the functio itself ?
var functionCaller = function(thisCaller, data) {
data = data + 1;
var nothing = function() {
thisCaller(thisCaller, data);
};
nothing();
};
functionCaller(functionCaller, data);
In certain situations you have to rely on anonymous functions. Given is a recursive map function:
const map = f => acc => ([head, ...tail]) => head === undefined
? acc
: map (f) ([...acc, f(head)]) (tail);
const sqr = x => x * x;
const xs = [1,2,3,4,5];
console.log(map(sqr) ([0]) (xs)); // [0] modifies the structure of the array
Please note that map must not modify the structure of the array. So the accumulator acc needn't to be exposed. We can wrap map into another function for instance:
const map = f => xs => {
let next = acc => ([head, ...tail]) => head === undefined
? acc
: map ([...acc, f(head)]) (tail);
return next([])(xs);
}
But this solution is quite verbose. Let's use the underestimated U combinator:
const U = f => f(f);
const map = f => U(h => acc => ([head, ...tail]) => head === undefined
? acc
: h(h)([...acc, f(head)])(tail))([]);
const sqr = x => x * x;
const xs = [1,2,3,4,5];
console.log(map(sqr) (xs));
Concise, isn't it? U is dead simple but has the disadvantage that the recursive call gets a bit obfuscated: sum(...) becomes h(h)(...) - that's all.
I am not sure if the answer is still required but this can also be done using delegates created using function.bind:
var x = ((function () {
return this.bind(this, arguments[0])();
}).bind(function (n) {
if (n != 1) {
return n * this.bind(this, (n - 1))();
}
else {
return 1;
}
}))(5);
console.log(x);
This does not involve named functions or arguments.callee.
With ES2015 we can play around a bit with the syntax and abuse default parameters and thunks. The latter are just functions without any arguments:
const applyT = thunk => thunk();
const fib = n => applyT(
(f = (x, y, n) => n === 0 ? x : f(y, x + y, n - 1)) => f(0, 1, n)
);
console.log(fib(10)); // 55
// Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55...
Please note that f is a parameter with the anonymous function (x, y, n) => n === 0 ? x : f(y, x + y, n - 1) as its default value. When f is invoked by applyT this invocation must take place without arguments, so that the default value is used. The default value is a function and hence f is a named function, which can call itself recursively.
Like bobince wrote, simply name your function.
But, I'm guessing you also want to pass in an initial value and stop your function eventually!
var initialValue = ...
(function recurse(data){
data++;
var nothing = function() {
recurse(data);
}
if ( ... stop condition ... )
{ ... display result, etc. ... }
else
nothing();
}(initialValue));
working jsFiddle example (uses data += data for fun)
i needed (or rather, wanted) an one-liner anonymous function to walk its way up an object building up a string, and handled it like this:
var cmTitle = 'Root' + (function cmCatRecurse(cmCat){return (cmCat == root) ? '' : cmCatRecurse(cmCat.parent) + ' : ' + cmCat.getDisplayName();})(cmCurrentCat);
which produces a string like 'Root : foo : bar : baz : ...'
Another answer which does not involve named function or arguments.callee
var sum = (function(foo,n){
return n + foo(foo,n-1);
})(function(foo,n){
if(n>1){
return n + foo(foo,n-1)
}else{
return n;
}
},5); //function takes two argument one is function and another is 5
console.log(sum) //output : 15
This is a rework of jforjs answer with different names and a slightly modified entry.
// function takes two argument: first is recursive function and second is input
var sum = (function(capturedRecurser,n){
return capturedRecurser(capturedRecurser, n);
})(function(thisFunction,n){
if(n>1){
return n + thisFunction(thisFunction,n-1)
}else{
return n;
}
},5);
console.log(sum) //output : 15
There was no need to unroll the first recursion. The function receiving itself as a reference harkens back to the primordial ooze of OOP.
This is a version of #zem's answer with arrow functions.
You can use the U or the Y combinator. Y combinator being the simplest to use.
U combinator, with this you have to keep passing the function:
const U = f => f(f)
U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))
Y combinator, with this you don't have to keep passing the function:
const Y = gen => U(f => gen((...args) => f(f)(...args)))
Y(selfFn => arg => selfFn('to infinity and beyond'))
Yet another Y-combinator solution, using rosetta-code link (I think somebody previously mentioned the link somewhere on stackOverflow.
Arrows are for anonymous functions more readable to me:
var Y = f => (x => x(x))(y => f(x => y(y)(x)));
I don't suggest doing this in any practical use-case, but just as a fun exercise, you can actually do this using a second anonymous function!
(f => f(f))(f => {
data = data+1;
var nothing = function() {
f();
}
nothing(f);
});
The way this works is that we're passing the anonymous function as an argument to itself, so we can call it from itself.
by using arguments.callee(). For more details visit this url: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#scope_and_the_function_stack
(function(data){
data = data+1;
var nothing = function() {
arguments.callee() // this calls the function itself
}
nothing();
})();
This may not work everywhere, but you can use arguments.callee to refer to the current function.
So, factorial could be done thus:
var fac = function(x) {
if (x == 1) return x;
else return x * arguments.callee(x-1);
}

Categories

Resources