Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrapping multiple optional values #8

Open
benadamstyles opened this issue Mar 31, 2017 · 8 comments
Open

Wrapping multiple optional values #8

benadamstyles opened this issue Mar 31, 2017 · 8 comments

Comments

@benadamstyles
Copy link
Contributor

What would the maybe version of the following be?

let valueX = null
let valueY = null

if (valueX && valueY) {
  // do something with valueX and valueY
}

Would it be this?

let valueX = null
let valueY = null

maybe(valueX)
.map(() => valueY)
.foreach(() => {
  // do something with valueX and valueY
})

This doesn't seem very functional. Maybe I'm missing something super obvious. Thanks!

@alexanderjarvis
Copy link
Owner

Unfortunately the syntax isn't too great:

maybe(valueX)
  .flatMap(x => maybe(valueY).map(y => ({x, y})))
  .foreach({x, y} => {
    // use x and y
  })

you can make it a tiny bit shorter by wrapping the values with maybe first e.g.

const maybeX = maybe(valueX)
const maybeY = maybe(valueY)

maybeX
  .flatMap(x => maybeY.map(y => ({x, y})))
  .foreach({x, y} => {
    // use x and y
  })

Something that Scala has which is quite nice for this is a for comprehension which would look like:

for {
  x <- maybeX
  y <- maybeY
  // do something with x, y / return result
} yield

...
but I think your if statement is fine :)

@benadamstyles
Copy link
Contributor Author

Ah ok, yep. I was close. One thing I'm struggling with actually is when to use .map and when to use .flatMap. I know there's the simple rule of "flatMap if you're returning a Maybe", but I'm not always sure whether I want to return a Maybe or a "possibly null value". Perhaps I never want to return a "possibly null value" – should I always be wrapping one of those in maybe()?

I wonder if there could be an extra method called something like and or join which would work something like:

maybe(valueX)
  .and(valueY)
  .and(valueZ)
  .foreach(([x, y, z]) => {
    // use x, y and z
  })

@benadamstyles
Copy link
Contributor Author

benadamstyles commented Mar 31, 2017

Just thought, couldn't you do this to make it even shorter? Given that you already know maybeX is a Maybe?

const maybeX = maybe(valueX)
const maybeY = maybe(valueY)

maybeX
  .flatMap(x => maybeY)
  .foreach(y => {
    const x = maybeX.just() // we know maybeX is a Just because it passed the flatMap test
    // use x and y
  })

@alexanderjarvis
Copy link
Owner

You use map when you transform the value inside the maybe to another value. flatMap is when you need to return a Maybe instead and is most useful unwrapping more than one Maybe or when using the just and nothing.

The best way to visualise and understand flatMap is to know that it's shorthand for flatten and would be the same as doing map and then flatten. For example:

maybeX.map(x => maybeY) // Maybe(Maybe(y))

whereas

maybeX.flatMap(x => maybeY) // Maybe(y)

so Maybe(Maybe(y)) becomes Maybe(y) by using flatMap.

@alexanderjarvis
Copy link
Owner

alexanderjarvis commented Mar 31, 2017

It would be possible to make another type of Maybe that takes an array and only lets you map / forEach when they're all non empty values.

For example:

maybe([1, 2, 3])
  .filter(a => a.every(e => maybe(e).isJust()))
  .foreach(console.log)

@alexanderjarvis
Copy link
Owner

Also in response to your last comment: you could do it like that. But I wouldn't.. as you're creating a dependency that might break if someone updates it. It's also perhaps less clear.

The preferred method would be to have default values for your Maybe's by using orJust().

@benadamstyles
Copy link
Contributor Author

This is so useful, thanks. I think I've finally "grokked" flatMap now... Thanks for all your help!

@benadamstyles
Copy link
Contributor Author

benadamstyles commented Apr 6, 2017

For example:

maybe([1, 2, 3])
 .filter(a => a.every(e => maybe(e).isJust()))
 .foreach(console.log)

@alexanderjarvis I'm finding myself doing this a lot. Would you consider a PR that adds a new method, something like .all() (or whatever you think) which does this? The code would be something along the lines of:

all() {
  if (Array.isArray(this.value) && this.value.every(x => maybe(x).isJust())) {
    return this
  } else {
    return nothing
  }
}

And you would use it like:

maybe([1, 2, 3])
  .all()
  .map(transform)
  .orJust([])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants