Skip to content

Conversation

@dwhitney
Copy link
Contributor

adds a modify combinator that simply unwraps the given newtype, applies a monomorphic function to it, and wraps it back in the newtype.

@garyb
Copy link
Member

garyb commented Oct 25, 2019

I don't think we need this since over does the same job, and infers better since it has the newtype constructor argument.

@dwhitney
Copy link
Contributor Author

dwhitney commented Oct 25, 2019

yes but you have to supply the type constructor as an argument:

over MyType (\a -> ...) t
vs
modify (\a -> ...) t

Personally I've never needed the over version of this, but I need modify constantly and always recreate it. FWIW @natefaubion agrees: https://functionalprogramming.slack.com/archives/C04NA444H/p1572019779382100

@natefaubion
Copy link

Over only requires the extra argument because it can't infer what newtype you are expecting as an input. Note that you don't need to specify the return newtype. If they are both the same, you have the same inference properties as over, don't you?

@dwhitney
Copy link
Contributor Author

Any chance of getting this merged? The argument against it is that over can do the same thing, but with more boilerplate. One of the main reasons to use this type class is convenience. modify is more convenient than over, given it requires less boilerplate. Why bother with the type class at all if not for the removal of boilerplate?

@garyb
Copy link
Member

garyb commented Oct 27, 2019

I personally wouldn't use this, for the same reason I use un X rather than unwrap in non-generic code, but I also don't feel that strongly about it that I'd block its inclusion, so I'll duck out of handling this any further. Nate has the power if he wants to include it 😄.

@hdgarrood
Copy link

My problem with this is that it would leave this library in a sort of inconsistent state where it can't quite decide what approach it is taking. At the moment, almost all of the functions provided by this library are very general, in that they take two Newtype constraints or two Functor constraints or both, and permit type-changing operations, in that they can return a different wrapped newtype to the one they received, and so on. So it seems the stance behind the design is that maximum generality is preferable over making simpler cases more ergonomic.

If we want to revisit that stance, then we should do it for the whole library, not just one function at a time, otherwise I worry that we will end up with a library which is more of a pain to use overall. For the avoidance of doubt I do think I could get behind this, as I also don't think I've ever used many of these functions in ways that made use of their full generality.

@dwhitney
Copy link
Contributor Author

dwhitney commented Nov 2, 2019

If we want to revisit that stance, then we should do it for the whole library, not just one function at a time

modify felt like something that was missing to me, not something that was philosophically different. unwrap and wrap feel philosophically the same as modify, and as you can see the implementation is simply the composition of them with a given function.

In Slack @natefaubion suggested I could create Data.Newtype.Mono if this PR were not accepted. I can do this, but I'm not sure what else I'd put there, and I don't really want to maintain a one function library, since I can simply add this to a Utils.purs I end up creating in most projects.

@hdgarrood
Copy link

modify felt like something that was missing to me, not something that was philosophically different. unwrap and wrap feel philosophically the same as modify, and as you can see the implementation is simply the composition of them with a given function.

Hm, I don't think I find this convincing. over already does everything that this modify does, albeit with a little more ceremony. unwrap and wrap are only really there because that's the most sensible way to implement the Newtype class, I don't think they are intended to be used directly (unless you're working with the class polymorphically). I'm pretty sure the intention is to use the newtype constructor itself directly for wrapping, and un for unwrapping.

In Slack @natefaubion suggested I could create Data.Newtype.Mono if this PR were not accepted. I can do this, but I'm not sure what else I'd put there, and I don't really want to maintain a one function library, since I can simply add this to a Utils.purs I end up creating in most projects.

It wouldn't (well, shouldn't) be a one function library, though; most of the other functions of this library also have two Newtype constraints, and could be simplified to only take one, while still remaining useful for the most common use cases. For example:

ala :: forall f t a s b. Functor f => Newtype t a => Newtype s b => (a -> t) -> ((b -> s) -> f t) -> f a

would become

ala :: forall f t a s b. Functor f => Newtype t a => (a -> t) -> ((a -> t) -> f t) -> f a

which I suspect still works for most use cases. It works for all of the examples given in the docs, at least:

ala Additive foldMap [1,2,3,4] -- 10
ala Multiplicative foldMap [1,2,3,4] -- 24
ala Conj foldMap [true, false] -- false
ala Disj foldMap [true, false] -- true

@natefaubion
Copy link

My problem with this is that it would leave this library in a sort of inconsistent state where it can't quite decide what approach it is taking.

Hm, I don't think I find this convincing. over already does everything that this modify does, albeit with a little more ceremony. unwrap and wrap are only really there because that's the most sensible way to implement the Newtype class, I don't think they are intended to be used directly (unless you're working with the class polymorphically). I'm pretty sure the intention is to use the newtype constructor itself directly for wrapping, and un for unwrapping.

I don't agree with this at all, and I don't find either of these convincing 😄. I use unwrap all the time. We have so many boilerplate newtypes in our codegenerated API bindings, it makes no sense bother with un. If it wasn't meant to be used then it's a bad abstraction. FWIW, I agree with the line of reasoning that modify is in the same vein as wrap and unwrap. It's a super common use case (at least for me), whereas the others aren't (for me).

@dwhitney
Copy link
Contributor Author

dwhitney commented Nov 2, 2019

over already does everything that this modify does

With pattern matching you can do about all that this library does. Like @natefaubion, I use wrap and unwrap all the time. They are useful. modify would be more useful.

@JordanMartinez
Copy link
Contributor

I was agreeing with Gary and Harry on this until I read Nate's comment. I think there is a place for modify.

I've also fixed the merge conflicts here.

@JordanMartinez JordanMartinez merged commit ddbe07e into purescript:master Oct 23, 2021
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

Successfully merging this pull request may close these issues.

6 participants