try {
var student = findStudent('444-44-4444');
} catch (e) {
console.log('ERROR' + e.message);
}
- Can’t be composed or chained.
- Side effects.
- Violate the principle of non-locality.
- Caller need to manage exceptions.
- Multiple exception-handling blocks.
Wrapping unsafe values (potentially erroneous value), so forcing developer to handle it.
class Wrapper {
constructor(value) {
this._value = value;
}
// map :: (A -> B) -> A -> B
map(f) {
return f(this.val);
}
toString() {
return 'Wrapper (' + this.value + ')';
}
}
// wrap :: A -> Wrapper(A)
const wrap = (val) => new Wrapper(val);
Functor is a powerful wrapper.
A functor is an object that can be mapped or has a map method.
And functor obeys the functor laws.
- The identity law:
functor.map(a => a)
is equivalent tofunctor
- The composition law:
functor.map(x => f(g(x)))
is equivalent tofunctor.map(g).map(f)
// Wrapper is a functor
// map :: (A -> B) -> Wrapper[A] -> Wrapper[B]
Wrapper.prototype.map = function (f) {
return wrap(f(this.val));
};
Functor and map method make sure:
- Transformation of contents
- Maintain structure
- Returns a new functor
- The purpose of having map return the same type is so you can continue chaining operations.
- They must be side effect–free.
- They must be composable.
- They prohibited from throwing exceptions, mutating elements, or altering a function’s behavior.
- Their practical purpose is to create a context/abstraction that allows you to securely manipulate and apply operations to values without changing any original values.
A functor is a function, given a value and a function, unwraps the values to get to its inner value(s), calls the given function with the inner value(s), wraps the returned values in a new structure, and returns the new structure.
http://functionaljavascript.blogspot.tw/2013/07/functors.html
This definitions is from category theory. But in FP language like Haskell, F#, Scala, etc., 95% definitions of functor is a object that can be mappable.
Functors, Applicatives, And Monads In Pictures
What-is-a-functor
Functors: I was WRONG! - FunFunFunction #11
Monad is like a more powerful functor that has a fmap (flatMap) method.
Monad should have the following interface for monadic operations
- Type constructor —Creates monadic types (Wrapper constructor)
- Unit function—Inserts a value of a certain type into a monadic structure (of method)
- Bind function —Chains operations together (fmap or flatMap)
- Join operation—Flattens layers of monadic structures into one.
class Wrapper {
// Type constructor
constructor(value) {
this._value = value;
}
// Unit function
static of(a) {
return new Wrapper(a);
}
// The functor
map(f) {
return Wrapper.of(f(this.value));
}
// Bind function ( f :: x -> OtherWrapper(x) )
fmap(f) {
return f(this.value);
}
// Flattens nested layers
join() {
if(!(this.value instanceof Wrapper)) {
return this;
}
return this.value.join();
}
}
please check the following link again. Functors, Applicatives, And Monads In Pictures
Wrapping IO (side effects) in monad.
const changeToStartCase =
IO.from(readDom('student-name')).
map(_.startCase).
map(writeDom('student-name'));
original handling
function showStudent(ssn) {
if(ssn != null) {
ssn = ssn.replace(/^\s*|\-|\s*$/g, '');
if(ssn.length !== 9) {
throw new Error('Invalid Input');
}
let student = db.get(ssn);
if (student) {
document.querySelector(`#${elementId}`).innerHTML =
`${student.ssn},
${student.firstname},
${student.lastname}`;
}
else {
throw new Error('Student not found!');
}
}
else {
throw new Error('Invalid SSN!');
}
}
handling by Monad
const showStudent = R.compose(
map(append('#student-info')),
liftIO,
map(csv),
map(R.props(['ssn', 'firstname', 'lastname'])),
chain(findStudent),
chain(checkLengthSsn),
lift(cleanInput)
);