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

assigning None through Optional.fromProp #7

Closed
leemhenson opened this issue May 31, 2017 · 6 comments
Closed

assigning None through Optional.fromProp #7

leemhenson opened this issue May 31, 2017 · 6 comments

Comments

@leemhenson
Copy link
Collaborator

Hi

I'm still getting my head around lenses, optionals lenses etc. I'm using this library in a situation where I want to use a lens to update deeply nested optional property to None. The Optional instance created via fromProp assumes that you always want to set a Some:

https://github.com/gcanti/monocle-ts/blob/master/src/index.ts#L171

  /** generate an optional from a type and a prop which is a `Option` */
  static fromProp<T extends { [K in P]: Option<any> }, P extends keyof T>(prop: P): Optional<T, T[P]['_A']> {
    return new Optional<T, T[P]['_A']>(
      s => s[prop],
      (a, s) => Object.assign({}, s, { [prop as any]: some(a) })
    )
  }

I guess I can write my own fromProp2:

  /** generate an optional from a type and a prop which is a `Option` */
  static fromProp2<T extends { [K in P]: Option<any> }, P extends keyof T>(prop: P): Optional<T, T[P]['_A']> {
    return new Optional<T, T[P]['_A']>(
      s => s[prop],
      (a, s) => Object.assign({}, s, { [prop as any]: fromNullable(a) })
    )
  }

but I wanted to check if there's a clever built-in mechanism I haven't picked up on instead?

@leemhenson
Copy link
Collaborator Author

I've knocked up a PR that makes my change. It should actually be safe to apply, right? Unless it invalidates some laws. This is where things get woolly for me. 😊

@gcanti
Copy link
Owner

gcanti commented May 31, 2017

Hi,

AFAIK Optionals are not "standard", they are a Monocle thing. Based on the documentation I think that the current implementation is not lawful, I guess should be

/** generate an optional from a type and a prop which is a `Option` */
static fromProp<T extends { [K in P]: Option<any> }, P extends keyof T>(prop: P): Optional<T, T[P]['_A']> {
  return new Optional<T, T[P]['_A']>(
    s => s[prop],
-    (a, s) => Object.assign({}, s, { [prop as any]: some(a) })
+    (a, s) => isSome(s[prop]) ? Object.assign({}, s, { [prop as any]: some(a) }) : s
  )
}

i.e. you cannot insert or delete with an Optional, only modify

@leemhenson
Copy link
Collaborator Author

Ah. So if want to be able to set something where there was None before, I actually need to use Lens<X, Option<Y>> instead of Optional<X, Y>?

@gcanti
Copy link
Owner

gcanti commented May 31, 2017

I think so, however is pretty awkward

import { Lens, Optional, Prism } from '../src'
import { Option, some, none } from 'fp-ts/lib/Option'

interface Bar {
  s: Option<string>
}

interface Foo {
  bar: Option<Bar>
}

const foo: Foo = {
  bar: some({
    // s: some('blah')
    s: none
  })
}

const barLens = Lens.fromProp<Foo, 'bar'>('bar')
const sLens = Lens.fromProp<Bar, 's'>('s')

console.log(barLens.modify(obar => obar.map(bar => sLens.set(some('BLAH'), bar)), foo)) // { bar: Some({"s":{"value":"BLAH","_tag":"Some"}}) }

Based on this 3D https://groups.google.com/forum/#!topic/scala-monocle/i7Y4o0I7tIc we could define the some prism

function getSomePrism<A>(): Prism<Option<A>, A> {
  return new Prism<Option<A>, A>(
    s => s,
    a => some(a)
  )
}

const sOptional = barLens.asOptional()
  .compose(getSomePrism<Bar>().asOptional())
  .compose(sLens.asOptional())
  .compose(getSomePrism<string>().asOptional())

console.log(sOptional.set('WHOP', foo)) // { bar: Some({"s":{"value":"WHOP","_tag":"Some"}}) }

Also we could add a bunch of composeX helpers so we can write

const sOptional = barLens
  .composePrism(getSomePrism<Bar>())
  .composeLens(sLens)
  .composePrism(getSomePrism<string>())

as shown in the link

@leemhenson
Copy link
Collaborator Author

Puts on reading glasses.

Thanks @gcanti !

@kylegoetz
Copy link

Trying to read through the comments and related ones in other issues, it does not appear Optional ever got the ability to set its target to none (i.e., clear the target). Was there a reason this wasn't implemented? I don't see discussion here or at #15 mentioning this, just a workaround involving a Lens<State, Option> rather than Optional<State, X>

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

3 participants