Is is possible to chain Maybe in case of null/undefined?

713 views Asked by At

There is a given function, that is fixed and must not be changed:

const validate = v => v === "fred" ? "Y" : undefined

Now, because I would like to be functional and would like to avoid null-checks I've decided to use Maybe (ramda-fantasy) for validation function:

const vv = val => Maybe(val).map(v=> validate(v)).getOrElse("N")

vv should return Y if it's called with "fred" otherwise N.

  • vv(null) returns N -> OK
  • vv("fred") returns Y -> OK
  • vv("ding") returns undefined -> wrong, expected N

The problem is, that Maybe.map always returns Just, that I do not understand (because I'm just learning it). For me I would be beneficial if this function would behave in similar way to Maybe(val) that returns None or Just.

I have two question:

  1. Why Maybe.map does not handle null/undefined?
  2. How to rewrite vv that it would return expected values in all three cases?

EDIT: I would like to explain why validate should not be changed: it's just simple example of function coming from external library. I wanted to see how easy/hard is to integrate such libraries into functional programming. So is not about string operations, just about streaming values when at some point it evaluates to null.

EDIT2:

This solves my problem:

Either.ofNullable = Either.prototype.ofNullable = function (value) {
    return value == null ? Either.Left("is null") : Either.Right(value);
};

EDIT3: I've implemented my own Either with missing functionality: https://github.com/maciejmiklas/functional-ts/blob/main/src/either.ts

1

There are 1 answers

5
Scott Sauyet On BEST ANSWER

Note: Ramda Fantasy is no longer maintained. The team recommends that you use other implementations of these concepts.


But we can still answer this question, as it's likely to be true of any reasonable Maybe implementation

Basically, that's not how Maybe is designed to work. The idea is that you can have a Just holding absolutely any value. That includes the values null and undefined.

Ramda added a convenience constructor, Maybe (val), which turns into Just (val) if val is not a nil value, and into Nothing () if it is. But that doesn't mean that you cannot create a Just (null). The main construction technique is to use the static Maybe .of. And you can note that

Maybe (null)    //=> Nothing ()
Maybe.of (null) //=> Just (null)

So we're probably not going to make that technique work. We couldn't just map such an existing validate over our Maybe and expect it to work. We could, however, work with a version like this:

const validate = v => v === "fred" ? Just ("Y") : Nothing ()

Here, we still have one problem. Maybe ('fred') .map (validate) yields Just (Just ('Y')). We have extra nesting. But this is exactly what chain is for. It removes such an additional level, so that Maybe ('fred') .chain (validate) yields Just ('Y'). We can then use it like this:

const validate = v => v === "fred" ? Just ("Y") : Nothing ()
const vv = val => Maybe (val) .chain (validate) .getOrElse ('N')

console .log ([
  vv (null),   // 'N'
  vv ('fred'), // 'Y' 
  vv ('ding')  // 'N'
])