prop
We write a simple prop
function which safely looks up properties on your object. If they are present, we get a Just
-wrapped value, otherwise we get Nothing
-
const fromNullable = x =>
x == null
? Nothing
: Just(x)
const prop = k => t =>
fromNullable(t[k])
props
You have nested properties though, so let's make it so we can dig arbitrarily deep into your object using any sequence of props
-
const props = (k, ...ks) => t =>
ks.length
? prop(k)(t).bind(props(...ks))
: prop(k)(t)
validate
Now we can write a simple validate
coroutine -
function* validate (t)
{ const id = yield props("user", "id")(t)
const username = yield props("user", "username")(t)
const email = yield props("user", "email")(t)
const post = yield props("post", "content")(t)
const date = yield props("post", "date")(t)
return Just({ id, username, email, post, date }) // <- Just(whateverYouWant)
}
const doc =
{ user:
{ id: '123'
, username: 'kibe'
, email: 'blabla@gmail.com'
}
, post:
{ content: 'my new post!'
, date: '20/02/2004'
}
}
coroutine(validate(doc)).bind(console.log)
With a valid doc
we see -
{
id: '123',
username: 'kibe',
email: 'blabla@gmail.com',
post: 'my new post!',
date: '20/02/2004'
}
With an invalid document, otherdoc
-
const otherdoc =
{ user:
{ id: '123'
, username: 'kibe'
, // <- missing email
}
, post:
{ content: 'my new post!'
, // <- missing date
}
}
coroutine(validate(otherdoc)).bind(console.log)
// no effect because validate returns a Nothing!
coroutine
Implementation of coroutine
is simple and most importantly it is generic for any monad -
function coroutine (f)
{ function next (v)
{ let {done, value} = f.next(v)
return done ? value : value.bind(next)
}
return next()
}
Maybe
Lastly we supply implementation for Nothing
and Just
-
const Nothing =
({ bind: _ => Nothing })
const Just = v =>
({ bind: f => f(v) })
demo
Expand the snippet below to verify the results in your own browser -
const Nothing =
({ bind: _ => Nothing })
const Just = v =>
({ bind: f => f(v) })
const fromNullable = x =>
x == null
? Nothing
: Just(x)
const prop = k => t =>
fromNullable(t[k])
const props = (k, ...ks) => t =>
ks.length
? prop(k)(t).bind(props(...ks))
: prop(k)(t)
function coroutine (f)
{ function next (v)
{ let {done, value} = f.next(v)
return done ? value : value.bind(next)
}
return next()
}
function* validate (t)
{ const id = yield props("user", "id")(t)
const username = yield props("user", "username")(t)
const email = yield props("user", "email")(t)
const post = yield props("post", "content")(t)
const date = yield props("post", "date")(t)
return Just({ id, username, email, post, date })
}
const doc =
{user:{id:'123',username:'kibe',email:'blabla@gmail.com'},post:{content:'my new post!',date:'20/02/2004'}}
coroutine(validate(doc)).bind(console.log)
related reading