Is there a shorthand for operations like `fromNewtype . f . toNewtype`?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
8
down vote

favorite
1












A pattern that presents itself the more often the more type safety is being introduced via newtype is to project a value (or several values) to a newtype wrapper, do some operations, and then retract the projection. An ubiquitous example is that of Sum and Product monoids:



λ x + y = getSum $ Sum x `mappend` Sum y
λ 1 + 2
3


I imagine a collection of functions like withSum, withSum2, and so on, may be automagically rolled out for each newtype. Or maybe a parametrized Identity may be created, for use with ApplicativeDo. Or maybe there are some other approaches that I could not think of.



I wonder if there is some prior art or theory around this.



P.S.   I am unhappy with coerce, for two reasons:




  • safety   I thought it is not very safe. After being pointed that it is actually safe, I
    tried a few things and I could not do anything harmful, because it requires a type annotation
    when there is a possibility of ambiguity. For example:



    λ newtype F = F Int deriving Show
    λ newtype G = G Int deriving Show
    λ coerce . (mappend (1 :: Sum Int)) . coerce $ F 1 :: G
    G 2
    λ coerce . (mappend (1 :: Product Int)) . coerce $ F 1 :: G
    G 1
    λ coerce . (mappend 1) . coerce $ F 1 :: G
    ...
    • Couldn't match representation of type ‘a0’ with that of ‘Int’
    arising from a use of ‘coerce’
    ...


    But I would still not welcome coerce, because it is far too easy to strip a safety label and
    shoot someone, once the reaching for it becomes habitual. Imagine that, in a cryptographic
    application, there are two values: x :: Prime Int and x' :: Sum Int. I would much rather
    type getPrime and getSum every time I use them, than coerce everything and have one day
    made a catastrophic mistake.




  • usefulness   coerce does not bring much to the table regarding a shorthand for
    certain operations. The leading example of my post, that I repeat here:



    λ getSum $ Sum 1 `mappend` Sum 2
    3


    — Turns into something along the lines of this spiked monster:



    λ coerce $ mappend @(Sum Integer) (coerce 1) (coerce 2) :: Integer
    3


    — Which is hardly of any benfit.








share|improve this question


















  • 2




    See my answer regarding your criticism of coerce as shorthand. You don't coerce the inputs and outputs (that is indeed little if any better than applying newtype constructors/getters); instead coerce the function you want to call so it has a different type. As for safety; it's no more or less safe than manually unwrapping. If you want to make that impossible, don't export the newtype constructors, and coerce won't work either. If you can mess up with coerce, you can also mess up with manual newtype unwrapping.
    – Ben
    Aug 19 at 9:36











  • I agree with your uneasiness about coerce -- though coerce won't do anything that you couldn't otherwise do safely. In particular, newtypes are not coercible unless their constructors are in scope. So if you are properly using smart constructors, for example, coerce can't hurt you.
    – luqui
    Aug 21 at 1:51















up vote
8
down vote

favorite
1












A pattern that presents itself the more often the more type safety is being introduced via newtype is to project a value (or several values) to a newtype wrapper, do some operations, and then retract the projection. An ubiquitous example is that of Sum and Product monoids:



λ x + y = getSum $ Sum x `mappend` Sum y
λ 1 + 2
3


I imagine a collection of functions like withSum, withSum2, and so on, may be automagically rolled out for each newtype. Or maybe a parametrized Identity may be created, for use with ApplicativeDo. Or maybe there are some other approaches that I could not think of.



I wonder if there is some prior art or theory around this.



P.S.   I am unhappy with coerce, for two reasons:




  • safety   I thought it is not very safe. After being pointed that it is actually safe, I
    tried a few things and I could not do anything harmful, because it requires a type annotation
    when there is a possibility of ambiguity. For example:



    λ newtype F = F Int deriving Show
    λ newtype G = G Int deriving Show
    λ coerce . (mappend (1 :: Sum Int)) . coerce $ F 1 :: G
    G 2
    λ coerce . (mappend (1 :: Product Int)) . coerce $ F 1 :: G
    G 1
    λ coerce . (mappend 1) . coerce $ F 1 :: G
    ...
    • Couldn't match representation of type ‘a0’ with that of ‘Int’
    arising from a use of ‘coerce’
    ...


    But I would still not welcome coerce, because it is far too easy to strip a safety label and
    shoot someone, once the reaching for it becomes habitual. Imagine that, in a cryptographic
    application, there are two values: x :: Prime Int and x' :: Sum Int. I would much rather
    type getPrime and getSum every time I use them, than coerce everything and have one day
    made a catastrophic mistake.




  • usefulness   coerce does not bring much to the table regarding a shorthand for
    certain operations. The leading example of my post, that I repeat here:



    λ getSum $ Sum 1 `mappend` Sum 2
    3


    — Turns into something along the lines of this spiked monster:



    λ coerce $ mappend @(Sum Integer) (coerce 1) (coerce 2) :: Integer
    3


    — Which is hardly of any benfit.








share|improve this question


















  • 2




    See my answer regarding your criticism of coerce as shorthand. You don't coerce the inputs and outputs (that is indeed little if any better than applying newtype constructors/getters); instead coerce the function you want to call so it has a different type. As for safety; it's no more or less safe than manually unwrapping. If you want to make that impossible, don't export the newtype constructors, and coerce won't work either. If you can mess up with coerce, you can also mess up with manual newtype unwrapping.
    – Ben
    Aug 19 at 9:36











  • I agree with your uneasiness about coerce -- though coerce won't do anything that you couldn't otherwise do safely. In particular, newtypes are not coercible unless their constructors are in scope. So if you are properly using smart constructors, for example, coerce can't hurt you.
    – luqui
    Aug 21 at 1:51













up vote
8
down vote

favorite
1









up vote
8
down vote

favorite
1






1





A pattern that presents itself the more often the more type safety is being introduced via newtype is to project a value (or several values) to a newtype wrapper, do some operations, and then retract the projection. An ubiquitous example is that of Sum and Product monoids:



λ x + y = getSum $ Sum x `mappend` Sum y
λ 1 + 2
3


I imagine a collection of functions like withSum, withSum2, and so on, may be automagically rolled out for each newtype. Or maybe a parametrized Identity may be created, for use with ApplicativeDo. Or maybe there are some other approaches that I could not think of.



I wonder if there is some prior art or theory around this.



P.S.   I am unhappy with coerce, for two reasons:




  • safety   I thought it is not very safe. After being pointed that it is actually safe, I
    tried a few things and I could not do anything harmful, because it requires a type annotation
    when there is a possibility of ambiguity. For example:



    λ newtype F = F Int deriving Show
    λ newtype G = G Int deriving Show
    λ coerce . (mappend (1 :: Sum Int)) . coerce $ F 1 :: G
    G 2
    λ coerce . (mappend (1 :: Product Int)) . coerce $ F 1 :: G
    G 1
    λ coerce . (mappend 1) . coerce $ F 1 :: G
    ...
    • Couldn't match representation of type ‘a0’ with that of ‘Int’
    arising from a use of ‘coerce’
    ...


    But I would still not welcome coerce, because it is far too easy to strip a safety label and
    shoot someone, once the reaching for it becomes habitual. Imagine that, in a cryptographic
    application, there are two values: x :: Prime Int and x' :: Sum Int. I would much rather
    type getPrime and getSum every time I use them, than coerce everything and have one day
    made a catastrophic mistake.




  • usefulness   coerce does not bring much to the table regarding a shorthand for
    certain operations. The leading example of my post, that I repeat here:



    λ getSum $ Sum 1 `mappend` Sum 2
    3


    — Turns into something along the lines of this spiked monster:



    λ coerce $ mappend @(Sum Integer) (coerce 1) (coerce 2) :: Integer
    3


    — Which is hardly of any benfit.








share|improve this question














A pattern that presents itself the more often the more type safety is being introduced via newtype is to project a value (or several values) to a newtype wrapper, do some operations, and then retract the projection. An ubiquitous example is that of Sum and Product monoids:



λ x + y = getSum $ Sum x `mappend` Sum y
λ 1 + 2
3


I imagine a collection of functions like withSum, withSum2, and so on, may be automagically rolled out for each newtype. Or maybe a parametrized Identity may be created, for use with ApplicativeDo. Or maybe there are some other approaches that I could not think of.



I wonder if there is some prior art or theory around this.



P.S.   I am unhappy with coerce, for two reasons:




  • safety   I thought it is not very safe. After being pointed that it is actually safe, I
    tried a few things and I could not do anything harmful, because it requires a type annotation
    when there is a possibility of ambiguity. For example:



    λ newtype F = F Int deriving Show
    λ newtype G = G Int deriving Show
    λ coerce . (mappend (1 :: Sum Int)) . coerce $ F 1 :: G
    G 2
    λ coerce . (mappend (1 :: Product Int)) . coerce $ F 1 :: G
    G 1
    λ coerce . (mappend 1) . coerce $ F 1 :: G
    ...
    • Couldn't match representation of type ‘a0’ with that of ‘Int’
    arising from a use of ‘coerce’
    ...


    But I would still not welcome coerce, because it is far too easy to strip a safety label and
    shoot someone, once the reaching for it becomes habitual. Imagine that, in a cryptographic
    application, there are two values: x :: Prime Int and x' :: Sum Int. I would much rather
    type getPrime and getSum every time I use them, than coerce everything and have one day
    made a catastrophic mistake.




  • usefulness   coerce does not bring much to the table regarding a shorthand for
    certain operations. The leading example of my post, that I repeat here:



    λ getSum $ Sum 1 `mappend` Sum 2
    3


    — Turns into something along the lines of this spiked monster:



    λ coerce $ mappend @(Sum Integer) (coerce 1) (coerce 2) :: Integer
    3


    — Which is hardly of any benfit.










share|improve this question













share|improve this question




share|improve this question








edited Aug 19 at 8:01

























asked Aug 19 at 6:47









Ignat Insarov

1,7321023




1,7321023







  • 2




    See my answer regarding your criticism of coerce as shorthand. You don't coerce the inputs and outputs (that is indeed little if any better than applying newtype constructors/getters); instead coerce the function you want to call so it has a different type. As for safety; it's no more or less safe than manually unwrapping. If you want to make that impossible, don't export the newtype constructors, and coerce won't work either. If you can mess up with coerce, you can also mess up with manual newtype unwrapping.
    – Ben
    Aug 19 at 9:36











  • I agree with your uneasiness about coerce -- though coerce won't do anything that you couldn't otherwise do safely. In particular, newtypes are not coercible unless their constructors are in scope. So if you are properly using smart constructors, for example, coerce can't hurt you.
    – luqui
    Aug 21 at 1:51













  • 2




    See my answer regarding your criticism of coerce as shorthand. You don't coerce the inputs and outputs (that is indeed little if any better than applying newtype constructors/getters); instead coerce the function you want to call so it has a different type. As for safety; it's no more or less safe than manually unwrapping. If you want to make that impossible, don't export the newtype constructors, and coerce won't work either. If you can mess up with coerce, you can also mess up with manual newtype unwrapping.
    – Ben
    Aug 19 at 9:36











  • I agree with your uneasiness about coerce -- though coerce won't do anything that you couldn't otherwise do safely. In particular, newtypes are not coercible unless their constructors are in scope. So if you are properly using smart constructors, for example, coerce can't hurt you.
    – luqui
    Aug 21 at 1:51








2




2




See my answer regarding your criticism of coerce as shorthand. You don't coerce the inputs and outputs (that is indeed little if any better than applying newtype constructors/getters); instead coerce the function you want to call so it has a different type. As for safety; it's no more or less safe than manually unwrapping. If you want to make that impossible, don't export the newtype constructors, and coerce won't work either. If you can mess up with coerce, you can also mess up with manual newtype unwrapping.
– Ben
Aug 19 at 9:36





See my answer regarding your criticism of coerce as shorthand. You don't coerce the inputs and outputs (that is indeed little if any better than applying newtype constructors/getters); instead coerce the function you want to call so it has a different type. As for safety; it's no more or less safe than manually unwrapping. If you want to make that impossible, don't export the newtype constructors, and coerce won't work either. If you can mess up with coerce, you can also mess up with manual newtype unwrapping.
– Ben
Aug 19 at 9:36













I agree with your uneasiness about coerce -- though coerce won't do anything that you couldn't otherwise do safely. In particular, newtypes are not coercible unless their constructors are in scope. So if you are properly using smart constructors, for example, coerce can't hurt you.
– luqui
Aug 21 at 1:51





I agree with your uneasiness about coerce -- though coerce won't do anything that you couldn't otherwise do safely. In particular, newtypes are not coercible unless their constructors are in scope. So if you are properly using smart constructors, for example, coerce can't hurt you.
– luqui
Aug 21 at 1:51













3 Answers
3






active

oldest

votes

















up vote
9
down vote













Your "spiked monster" example is better handled by putting the summands into a list and using the ala function available here, which has type:



ala :: (Coercible a b, Coercible a' b') 
=> (a -> b)
-> ((a -> b) -> c -> b')
-> c
-> a'


where




  • a is the unwrapped base type.


  • b is the newtype that wraps a.


  • a -> b is the newtype constructor.


  • ((a -> b) -> c -> b') is a function that, knowing how to wrap values of the base type a, knows how to process a value of type c (almost always a container of as) and return a wrapped result b'. In practice this function is almost always foldMap.


  • a' the unwrapped final result. The unwrapping is handled by ala itself.

in your case, it would be something like:



ala Sum foldMap [1,2::Integer]


"ala" functions can be implemented through means other than coerce, for example using generics to handle the unwrapping, or even lenses.






share|improve this answer


















  • 1




    I'm happy to see coercible-utils mentioned here! :) Would you mind changing the link to ala to hackage.haskell.org/package/coercible-utils-0.0.0/docs/… though? This way it's more clear that coercible-utils is available from Hackage.
    – sjakobi
    Aug 20 at 17:26

















up vote
6
down vote













coerce from Data.Coerce can be pretty great for this sort of thing. You can use it to convert between different types with the same representation (like between a type and a newtype wrapper, or vice versa). For example:



λ coerce (3 :: Int) :: Sum Int
Sum getSum = 3
it :: Sum Int

λ coerce (3 :: Sum Int) :: Int
3
it :: Int


It was developed to solve the problem that it is cost-free to e.g. convert an Int into a Sum Int by applying Sum, but it isn't necessarily cost-free to e.g convert a [Int] to a [Sum Int] by applying map Sum. The compiler might be able to optimise away the traversal of the list spine from map or it might not, but we know that the same structure in memory can serve as either a [Int] or a [Sum Int], because the list structure doesn't depend on any properties of the elements and the element types have identical representation between those two cases. coerce (plus the role system) allows us to make use of this fact to convert between the two in a way that is guaranteed not to do any runtime work, but still have the compiler check that it's safe to do so:



λ coerce [1, 2, 3 :: Int] :: [Sum Int]
[Sum getSum = 1,Sum getSum = 2,Sum getSum = 3]
it :: [Sum Int]


Something that wasn't at all obvious to me at first is that coerce is not limited to coercing "structures"! Because all it's doing is allowing us to substitute types (including parts of compound types) when the representations are identical, it works just as well to coerce code:



λ addInt = (+) @ Int
addInt :: Int -> Int -> Int

λ let addSum :: Sum Int -> Sum Int -> Sum Int
| addSum = coerce addInt
|
addSum :: Sum Int -> Sum Int -> Sum Int

λ addSum (Sum 3) (Sum 19)
Sum getSum = 22
it :: Sum Int


(In the above example I had to define a monotype version of + because coerce is so generic the type system otherwise doesn't know which version of + I'm asking to coerce to Sum Int -> Sum Int -> Sum Int; I could instead have given an inline type signature on the argument to coerce, but that looks less tidy. Often in real usage the context is sufficient to determine the "source" and "target" types of the coerce)



I once wrote a library that provided a few different ways of paramterising types via newtypes, and provided similar APIs with each scheme. The modules implementing the APIs were full of type signatures and foo' = coerce foo style definitions; it felt really nice that I was barely doing any work other than stating the types that I wanted.



Your example (using mappend on Sum to implement addition, without having to explicitly convert back and forth) could look like:



λ let (+) :: Int -> Int -> Int
| (+) = coerce (mappend @ (Sum Int))
|
(+) :: Int -> Int -> Int

λ 3 + 8
11
it :: Int





share|improve this answer





























    up vote
    6
    down vote













    Yes, there's! It's a coerce function from base package. It allows to convert from newtype and to newtype automatically. GHC actually has a big chunk of theory behind coercions.



    In relude I called this function under.



    ghci> newtype Foo = Foo Bool deriving Show
    ghci> under not (Foo True)
    Foo False
    ghci> newtype Bar = Bar String deriving Show
    ghci> under (filter (== 'a')) (Bar "abacaba")
    Bar "aaaa"


    You can see the whole module here:



    • http://hackage.haskell.org/package/relude-0.2.0/docs/Relude-Extra-Newtype.html

    It's also possible to implement custom functions for binary operators as well:



    ghci> import Data.Coerce 
    ghci> :set -XScopedTypeVariables
    ghci> :set -XTypeApplications
    ghci> : via = coerce
    ghci
    ghci> :
    ghci
    ghci> via @(Sum Int) @Int (<>) 3 4
    7
    ghci> viaF @Sum @Int (<>) 3 5
    8





    share|improve this answer






















    • I should have mentioned that coerce is unsuitable for this purpose, since it defies type safety.
      – Ignat Insarov
      Aug 19 at 7:13










    • @IgnatInsarov Why does coerce defy type safety? Are you sure you're not thinking of unsafeCoerce?
      – David Young
      Aug 19 at 7:13






    • 4




      @IgnatInsarov This is a common misconception. coerce is completely safe. You can't coerce integer to string for example. It's possible with unsafeCoerce only.
      – Shersh
      Aug 19 at 7:14











    • @DavidYoung Not like totally defies. It is just that I do not trust myself with it. See the postscriptum I added to my question.
      – Ignat Insarov
      Aug 19 at 8:03










    • @IgnatInsarov It's possible to implement function like via @Sum @Int (<>) 3 4 which results into 3 + 4 using coerce.
      – Shersh
      Aug 19 at 8:08











    Your Answer





    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51915219%2fis-there-a-shorthand-for-operations-like-fromnewtype-f-tonewtype%23new-answer', 'question_page');

    );

    Post as a guest






























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    9
    down vote













    Your "spiked monster" example is better handled by putting the summands into a list and using the ala function available here, which has type:



    ala :: (Coercible a b, Coercible a' b') 
    => (a -> b)
    -> ((a -> b) -> c -> b')
    -> c
    -> a'


    where




    • a is the unwrapped base type.


    • b is the newtype that wraps a.


    • a -> b is the newtype constructor.


    • ((a -> b) -> c -> b') is a function that, knowing how to wrap values of the base type a, knows how to process a value of type c (almost always a container of as) and return a wrapped result b'. In practice this function is almost always foldMap.


    • a' the unwrapped final result. The unwrapping is handled by ala itself.

    in your case, it would be something like:



    ala Sum foldMap [1,2::Integer]


    "ala" functions can be implemented through means other than coerce, for example using generics to handle the unwrapping, or even lenses.






    share|improve this answer


















    • 1




      I'm happy to see coercible-utils mentioned here! :) Would you mind changing the link to ala to hackage.haskell.org/package/coercible-utils-0.0.0/docs/… though? This way it's more clear that coercible-utils is available from Hackage.
      – sjakobi
      Aug 20 at 17:26














    up vote
    9
    down vote













    Your "spiked monster" example is better handled by putting the summands into a list and using the ala function available here, which has type:



    ala :: (Coercible a b, Coercible a' b') 
    => (a -> b)
    -> ((a -> b) -> c -> b')
    -> c
    -> a'


    where




    • a is the unwrapped base type.


    • b is the newtype that wraps a.


    • a -> b is the newtype constructor.


    • ((a -> b) -> c -> b') is a function that, knowing how to wrap values of the base type a, knows how to process a value of type c (almost always a container of as) and return a wrapped result b'. In practice this function is almost always foldMap.


    • a' the unwrapped final result. The unwrapping is handled by ala itself.

    in your case, it would be something like:



    ala Sum foldMap [1,2::Integer]


    "ala" functions can be implemented through means other than coerce, for example using generics to handle the unwrapping, or even lenses.






    share|improve this answer


















    • 1




      I'm happy to see coercible-utils mentioned here! :) Would you mind changing the link to ala to hackage.haskell.org/package/coercible-utils-0.0.0/docs/… though? This way it's more clear that coercible-utils is available from Hackage.
      – sjakobi
      Aug 20 at 17:26












    up vote
    9
    down vote










    up vote
    9
    down vote









    Your "spiked monster" example is better handled by putting the summands into a list and using the ala function available here, which has type:



    ala :: (Coercible a b, Coercible a' b') 
    => (a -> b)
    -> ((a -> b) -> c -> b')
    -> c
    -> a'


    where




    • a is the unwrapped base type.


    • b is the newtype that wraps a.


    • a -> b is the newtype constructor.


    • ((a -> b) -> c -> b') is a function that, knowing how to wrap values of the base type a, knows how to process a value of type c (almost always a container of as) and return a wrapped result b'. In practice this function is almost always foldMap.


    • a' the unwrapped final result. The unwrapping is handled by ala itself.

    in your case, it would be something like:



    ala Sum foldMap [1,2::Integer]


    "ala" functions can be implemented through means other than coerce, for example using generics to handle the unwrapping, or even lenses.






    share|improve this answer














    Your "spiked monster" example is better handled by putting the summands into a list and using the ala function available here, which has type:



    ala :: (Coercible a b, Coercible a' b') 
    => (a -> b)
    -> ((a -> b) -> c -> b')
    -> c
    -> a'


    where




    • a is the unwrapped base type.


    • b is the newtype that wraps a.


    • a -> b is the newtype constructor.


    • ((a -> b) -> c -> b') is a function that, knowing how to wrap values of the base type a, knows how to process a value of type c (almost always a container of as) and return a wrapped result b'. In practice this function is almost always foldMap.


    • a' the unwrapped final result. The unwrapping is handled by ala itself.

    in your case, it would be something like:



    ala Sum foldMap [1,2::Integer]


    "ala" functions can be implemented through means other than coerce, for example using generics to handle the unwrapping, or even lenses.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Aug 20 at 18:16

























    answered Aug 19 at 9:12









    danidiaz

    16k32655




    16k32655







    • 1




      I'm happy to see coercible-utils mentioned here! :) Would you mind changing the link to ala to hackage.haskell.org/package/coercible-utils-0.0.0/docs/… though? This way it's more clear that coercible-utils is available from Hackage.
      – sjakobi
      Aug 20 at 17:26












    • 1




      I'm happy to see coercible-utils mentioned here! :) Would you mind changing the link to ala to hackage.haskell.org/package/coercible-utils-0.0.0/docs/… though? This way it's more clear that coercible-utils is available from Hackage.
      – sjakobi
      Aug 20 at 17:26







    1




    1




    I'm happy to see coercible-utils mentioned here! :) Would you mind changing the link to ala to hackage.haskell.org/package/coercible-utils-0.0.0/docs/… though? This way it's more clear that coercible-utils is available from Hackage.
    – sjakobi
    Aug 20 at 17:26




    I'm happy to see coercible-utils mentioned here! :) Would you mind changing the link to ala to hackage.haskell.org/package/coercible-utils-0.0.0/docs/… though? This way it's more clear that coercible-utils is available from Hackage.
    – sjakobi
    Aug 20 at 17:26












    up vote
    6
    down vote













    coerce from Data.Coerce can be pretty great for this sort of thing. You can use it to convert between different types with the same representation (like between a type and a newtype wrapper, or vice versa). For example:



    λ coerce (3 :: Int) :: Sum Int
    Sum getSum = 3
    it :: Sum Int

    λ coerce (3 :: Sum Int) :: Int
    3
    it :: Int


    It was developed to solve the problem that it is cost-free to e.g. convert an Int into a Sum Int by applying Sum, but it isn't necessarily cost-free to e.g convert a [Int] to a [Sum Int] by applying map Sum. The compiler might be able to optimise away the traversal of the list spine from map or it might not, but we know that the same structure in memory can serve as either a [Int] or a [Sum Int], because the list structure doesn't depend on any properties of the elements and the element types have identical representation between those two cases. coerce (plus the role system) allows us to make use of this fact to convert between the two in a way that is guaranteed not to do any runtime work, but still have the compiler check that it's safe to do so:



    λ coerce [1, 2, 3 :: Int] :: [Sum Int]
    [Sum getSum = 1,Sum getSum = 2,Sum getSum = 3]
    it :: [Sum Int]


    Something that wasn't at all obvious to me at first is that coerce is not limited to coercing "structures"! Because all it's doing is allowing us to substitute types (including parts of compound types) when the representations are identical, it works just as well to coerce code:



    λ addInt = (+) @ Int
    addInt :: Int -> Int -> Int

    λ let addSum :: Sum Int -> Sum Int -> Sum Int
    | addSum = coerce addInt
    |
    addSum :: Sum Int -> Sum Int -> Sum Int

    λ addSum (Sum 3) (Sum 19)
    Sum getSum = 22
    it :: Sum Int


    (In the above example I had to define a monotype version of + because coerce is so generic the type system otherwise doesn't know which version of + I'm asking to coerce to Sum Int -> Sum Int -> Sum Int; I could instead have given an inline type signature on the argument to coerce, but that looks less tidy. Often in real usage the context is sufficient to determine the "source" and "target" types of the coerce)



    I once wrote a library that provided a few different ways of paramterising types via newtypes, and provided similar APIs with each scheme. The modules implementing the APIs were full of type signatures and foo' = coerce foo style definitions; it felt really nice that I was barely doing any work other than stating the types that I wanted.



    Your example (using mappend on Sum to implement addition, without having to explicitly convert back and forth) could look like:



    λ let (+) :: Int -> Int -> Int
    | (+) = coerce (mappend @ (Sum Int))
    |
    (+) :: Int -> Int -> Int

    λ 3 + 8
    11
    it :: Int





    share|improve this answer


























      up vote
      6
      down vote













      coerce from Data.Coerce can be pretty great for this sort of thing. You can use it to convert between different types with the same representation (like between a type and a newtype wrapper, or vice versa). For example:



      λ coerce (3 :: Int) :: Sum Int
      Sum getSum = 3
      it :: Sum Int

      λ coerce (3 :: Sum Int) :: Int
      3
      it :: Int


      It was developed to solve the problem that it is cost-free to e.g. convert an Int into a Sum Int by applying Sum, but it isn't necessarily cost-free to e.g convert a [Int] to a [Sum Int] by applying map Sum. The compiler might be able to optimise away the traversal of the list spine from map or it might not, but we know that the same structure in memory can serve as either a [Int] or a [Sum Int], because the list structure doesn't depend on any properties of the elements and the element types have identical representation between those two cases. coerce (plus the role system) allows us to make use of this fact to convert between the two in a way that is guaranteed not to do any runtime work, but still have the compiler check that it's safe to do so:



      λ coerce [1, 2, 3 :: Int] :: [Sum Int]
      [Sum getSum = 1,Sum getSum = 2,Sum getSum = 3]
      it :: [Sum Int]


      Something that wasn't at all obvious to me at first is that coerce is not limited to coercing "structures"! Because all it's doing is allowing us to substitute types (including parts of compound types) when the representations are identical, it works just as well to coerce code:



      λ addInt = (+) @ Int
      addInt :: Int -> Int -> Int

      λ let addSum :: Sum Int -> Sum Int -> Sum Int
      | addSum = coerce addInt
      |
      addSum :: Sum Int -> Sum Int -> Sum Int

      λ addSum (Sum 3) (Sum 19)
      Sum getSum = 22
      it :: Sum Int


      (In the above example I had to define a monotype version of + because coerce is so generic the type system otherwise doesn't know which version of + I'm asking to coerce to Sum Int -> Sum Int -> Sum Int; I could instead have given an inline type signature on the argument to coerce, but that looks less tidy. Often in real usage the context is sufficient to determine the "source" and "target" types of the coerce)



      I once wrote a library that provided a few different ways of paramterising types via newtypes, and provided similar APIs with each scheme. The modules implementing the APIs were full of type signatures and foo' = coerce foo style definitions; it felt really nice that I was barely doing any work other than stating the types that I wanted.



      Your example (using mappend on Sum to implement addition, without having to explicitly convert back and forth) could look like:



      λ let (+) :: Int -> Int -> Int
      | (+) = coerce (mappend @ (Sum Int))
      |
      (+) :: Int -> Int -> Int

      λ 3 + 8
      11
      it :: Int





      share|improve this answer
























        up vote
        6
        down vote










        up vote
        6
        down vote









        coerce from Data.Coerce can be pretty great for this sort of thing. You can use it to convert between different types with the same representation (like between a type and a newtype wrapper, or vice versa). For example:



        λ coerce (3 :: Int) :: Sum Int
        Sum getSum = 3
        it :: Sum Int

        λ coerce (3 :: Sum Int) :: Int
        3
        it :: Int


        It was developed to solve the problem that it is cost-free to e.g. convert an Int into a Sum Int by applying Sum, but it isn't necessarily cost-free to e.g convert a [Int] to a [Sum Int] by applying map Sum. The compiler might be able to optimise away the traversal of the list spine from map or it might not, but we know that the same structure in memory can serve as either a [Int] or a [Sum Int], because the list structure doesn't depend on any properties of the elements and the element types have identical representation between those two cases. coerce (plus the role system) allows us to make use of this fact to convert between the two in a way that is guaranteed not to do any runtime work, but still have the compiler check that it's safe to do so:



        λ coerce [1, 2, 3 :: Int] :: [Sum Int]
        [Sum getSum = 1,Sum getSum = 2,Sum getSum = 3]
        it :: [Sum Int]


        Something that wasn't at all obvious to me at first is that coerce is not limited to coercing "structures"! Because all it's doing is allowing us to substitute types (including parts of compound types) when the representations are identical, it works just as well to coerce code:



        λ addInt = (+) @ Int
        addInt :: Int -> Int -> Int

        λ let addSum :: Sum Int -> Sum Int -> Sum Int
        | addSum = coerce addInt
        |
        addSum :: Sum Int -> Sum Int -> Sum Int

        λ addSum (Sum 3) (Sum 19)
        Sum getSum = 22
        it :: Sum Int


        (In the above example I had to define a monotype version of + because coerce is so generic the type system otherwise doesn't know which version of + I'm asking to coerce to Sum Int -> Sum Int -> Sum Int; I could instead have given an inline type signature on the argument to coerce, but that looks less tidy. Often in real usage the context is sufficient to determine the "source" and "target" types of the coerce)



        I once wrote a library that provided a few different ways of paramterising types via newtypes, and provided similar APIs with each scheme. The modules implementing the APIs were full of type signatures and foo' = coerce foo style definitions; it felt really nice that I was barely doing any work other than stating the types that I wanted.



        Your example (using mappend on Sum to implement addition, without having to explicitly convert back and forth) could look like:



        λ let (+) :: Int -> Int -> Int
        | (+) = coerce (mappend @ (Sum Int))
        |
        (+) :: Int -> Int -> Int

        λ 3 + 8
        11
        it :: Int





        share|improve this answer














        coerce from Data.Coerce can be pretty great for this sort of thing. You can use it to convert between different types with the same representation (like between a type and a newtype wrapper, or vice versa). For example:



        λ coerce (3 :: Int) :: Sum Int
        Sum getSum = 3
        it :: Sum Int

        λ coerce (3 :: Sum Int) :: Int
        3
        it :: Int


        It was developed to solve the problem that it is cost-free to e.g. convert an Int into a Sum Int by applying Sum, but it isn't necessarily cost-free to e.g convert a [Int] to a [Sum Int] by applying map Sum. The compiler might be able to optimise away the traversal of the list spine from map or it might not, but we know that the same structure in memory can serve as either a [Int] or a [Sum Int], because the list structure doesn't depend on any properties of the elements and the element types have identical representation between those two cases. coerce (plus the role system) allows us to make use of this fact to convert between the two in a way that is guaranteed not to do any runtime work, but still have the compiler check that it's safe to do so:



        λ coerce [1, 2, 3 :: Int] :: [Sum Int]
        [Sum getSum = 1,Sum getSum = 2,Sum getSum = 3]
        it :: [Sum Int]


        Something that wasn't at all obvious to me at first is that coerce is not limited to coercing "structures"! Because all it's doing is allowing us to substitute types (including parts of compound types) when the representations are identical, it works just as well to coerce code:



        λ addInt = (+) @ Int
        addInt :: Int -> Int -> Int

        λ let addSum :: Sum Int -> Sum Int -> Sum Int
        | addSum = coerce addInt
        |
        addSum :: Sum Int -> Sum Int -> Sum Int

        λ addSum (Sum 3) (Sum 19)
        Sum getSum = 22
        it :: Sum Int


        (In the above example I had to define a monotype version of + because coerce is so generic the type system otherwise doesn't know which version of + I'm asking to coerce to Sum Int -> Sum Int -> Sum Int; I could instead have given an inline type signature on the argument to coerce, but that looks less tidy. Often in real usage the context is sufficient to determine the "source" and "target" types of the coerce)



        I once wrote a library that provided a few different ways of paramterising types via newtypes, and provided similar APIs with each scheme. The modules implementing the APIs were full of type signatures and foo' = coerce foo style definitions; it felt really nice that I was barely doing any work other than stating the types that I wanted.



        Your example (using mappend on Sum to implement addition, without having to explicitly convert back and forth) could look like:



        λ let (+) :: Int -> Int -> Int
        | (+) = coerce (mappend @ (Sum Int))
        |
        (+) :: Int -> Int -> Int

        λ 3 + 8
        11
        it :: Int






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Aug 19 at 7:33

























        answered Aug 19 at 7:20









        Ben

        44.4k1291135




        44.4k1291135




















            up vote
            6
            down vote













            Yes, there's! It's a coerce function from base package. It allows to convert from newtype and to newtype automatically. GHC actually has a big chunk of theory behind coercions.



            In relude I called this function under.



            ghci> newtype Foo = Foo Bool deriving Show
            ghci> under not (Foo True)
            Foo False
            ghci> newtype Bar = Bar String deriving Show
            ghci> under (filter (== 'a')) (Bar "abacaba")
            Bar "aaaa"


            You can see the whole module here:



            • http://hackage.haskell.org/package/relude-0.2.0/docs/Relude-Extra-Newtype.html

            It's also possible to implement custom functions for binary operators as well:



            ghci> import Data.Coerce 
            ghci> :set -XScopedTypeVariables
            ghci> :set -XTypeApplications
            ghci> : via = coerce
            ghci
            ghci> :
            ghci
            ghci> via @(Sum Int) @Int (<>) 3 4
            7
            ghci> viaF @Sum @Int (<>) 3 5
            8





            share|improve this answer






















            • I should have mentioned that coerce is unsuitable for this purpose, since it defies type safety.
              – Ignat Insarov
              Aug 19 at 7:13










            • @IgnatInsarov Why does coerce defy type safety? Are you sure you're not thinking of unsafeCoerce?
              – David Young
              Aug 19 at 7:13






            • 4




              @IgnatInsarov This is a common misconception. coerce is completely safe. You can't coerce integer to string for example. It's possible with unsafeCoerce only.
              – Shersh
              Aug 19 at 7:14











            • @DavidYoung Not like totally defies. It is just that I do not trust myself with it. See the postscriptum I added to my question.
              – Ignat Insarov
              Aug 19 at 8:03










            • @IgnatInsarov It's possible to implement function like via @Sum @Int (<>) 3 4 which results into 3 + 4 using coerce.
              – Shersh
              Aug 19 at 8:08















            up vote
            6
            down vote













            Yes, there's! It's a coerce function from base package. It allows to convert from newtype and to newtype automatically. GHC actually has a big chunk of theory behind coercions.



            In relude I called this function under.



            ghci> newtype Foo = Foo Bool deriving Show
            ghci> under not (Foo True)
            Foo False
            ghci> newtype Bar = Bar String deriving Show
            ghci> under (filter (== 'a')) (Bar "abacaba")
            Bar "aaaa"


            You can see the whole module here:



            • http://hackage.haskell.org/package/relude-0.2.0/docs/Relude-Extra-Newtype.html

            It's also possible to implement custom functions for binary operators as well:



            ghci> import Data.Coerce 
            ghci> :set -XScopedTypeVariables
            ghci> :set -XTypeApplications
            ghci> : via = coerce
            ghci
            ghci> :
            ghci
            ghci> via @(Sum Int) @Int (<>) 3 4
            7
            ghci> viaF @Sum @Int (<>) 3 5
            8





            share|improve this answer






















            • I should have mentioned that coerce is unsuitable for this purpose, since it defies type safety.
              – Ignat Insarov
              Aug 19 at 7:13










            • @IgnatInsarov Why does coerce defy type safety? Are you sure you're not thinking of unsafeCoerce?
              – David Young
              Aug 19 at 7:13






            • 4




              @IgnatInsarov This is a common misconception. coerce is completely safe. You can't coerce integer to string for example. It's possible with unsafeCoerce only.
              – Shersh
              Aug 19 at 7:14











            • @DavidYoung Not like totally defies. It is just that I do not trust myself with it. See the postscriptum I added to my question.
              – Ignat Insarov
              Aug 19 at 8:03










            • @IgnatInsarov It's possible to implement function like via @Sum @Int (<>) 3 4 which results into 3 + 4 using coerce.
              – Shersh
              Aug 19 at 8:08













            up vote
            6
            down vote










            up vote
            6
            down vote









            Yes, there's! It's a coerce function from base package. It allows to convert from newtype and to newtype automatically. GHC actually has a big chunk of theory behind coercions.



            In relude I called this function under.



            ghci> newtype Foo = Foo Bool deriving Show
            ghci> under not (Foo True)
            Foo False
            ghci> newtype Bar = Bar String deriving Show
            ghci> under (filter (== 'a')) (Bar "abacaba")
            Bar "aaaa"


            You can see the whole module here:



            • http://hackage.haskell.org/package/relude-0.2.0/docs/Relude-Extra-Newtype.html

            It's also possible to implement custom functions for binary operators as well:



            ghci> import Data.Coerce 
            ghci> :set -XScopedTypeVariables
            ghci> :set -XTypeApplications
            ghci> : via = coerce
            ghci
            ghci> :
            ghci
            ghci> via @(Sum Int) @Int (<>) 3 4
            7
            ghci> viaF @Sum @Int (<>) 3 5
            8





            share|improve this answer














            Yes, there's! It's a coerce function from base package. It allows to convert from newtype and to newtype automatically. GHC actually has a big chunk of theory behind coercions.



            In relude I called this function under.



            ghci> newtype Foo = Foo Bool deriving Show
            ghci> under not (Foo True)
            Foo False
            ghci> newtype Bar = Bar String deriving Show
            ghci> under (filter (== 'a')) (Bar "abacaba")
            Bar "aaaa"


            You can see the whole module here:



            • http://hackage.haskell.org/package/relude-0.2.0/docs/Relude-Extra-Newtype.html

            It's also possible to implement custom functions for binary operators as well:



            ghci> import Data.Coerce 
            ghci> :set -XScopedTypeVariables
            ghci> :set -XTypeApplications
            ghci> : via = coerce
            ghci
            ghci> :
            ghci
            ghci> via @(Sum Int) @Int (<>) 3 4
            7
            ghci> viaF @Sum @Int (<>) 3 5
            8






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Aug 19 at 15:41

























            answered Aug 19 at 7:11









            Shersh

            5,28831439




            5,28831439











            • I should have mentioned that coerce is unsuitable for this purpose, since it defies type safety.
              – Ignat Insarov
              Aug 19 at 7:13










            • @IgnatInsarov Why does coerce defy type safety? Are you sure you're not thinking of unsafeCoerce?
              – David Young
              Aug 19 at 7:13






            • 4




              @IgnatInsarov This is a common misconception. coerce is completely safe. You can't coerce integer to string for example. It's possible with unsafeCoerce only.
              – Shersh
              Aug 19 at 7:14











            • @DavidYoung Not like totally defies. It is just that I do not trust myself with it. See the postscriptum I added to my question.
              – Ignat Insarov
              Aug 19 at 8:03










            • @IgnatInsarov It's possible to implement function like via @Sum @Int (<>) 3 4 which results into 3 + 4 using coerce.
              – Shersh
              Aug 19 at 8:08

















            • I should have mentioned that coerce is unsuitable for this purpose, since it defies type safety.
              – Ignat Insarov
              Aug 19 at 7:13










            • @IgnatInsarov Why does coerce defy type safety? Are you sure you're not thinking of unsafeCoerce?
              – David Young
              Aug 19 at 7:13






            • 4




              @IgnatInsarov This is a common misconception. coerce is completely safe. You can't coerce integer to string for example. It's possible with unsafeCoerce only.
              – Shersh
              Aug 19 at 7:14











            • @DavidYoung Not like totally defies. It is just that I do not trust myself with it. See the postscriptum I added to my question.
              – Ignat Insarov
              Aug 19 at 8:03










            • @IgnatInsarov It's possible to implement function like via @Sum @Int (<>) 3 4 which results into 3 + 4 using coerce.
              – Shersh
              Aug 19 at 8:08
















            I should have mentioned that coerce is unsuitable for this purpose, since it defies type safety.
            – Ignat Insarov
            Aug 19 at 7:13




            I should have mentioned that coerce is unsuitable for this purpose, since it defies type safety.
            – Ignat Insarov
            Aug 19 at 7:13












            @IgnatInsarov Why does coerce defy type safety? Are you sure you're not thinking of unsafeCoerce?
            – David Young
            Aug 19 at 7:13




            @IgnatInsarov Why does coerce defy type safety? Are you sure you're not thinking of unsafeCoerce?
            – David Young
            Aug 19 at 7:13




            4




            4




            @IgnatInsarov This is a common misconception. coerce is completely safe. You can't coerce integer to string for example. It's possible with unsafeCoerce only.
            – Shersh
            Aug 19 at 7:14





            @IgnatInsarov This is a common misconception. coerce is completely safe. You can't coerce integer to string for example. It's possible with unsafeCoerce only.
            – Shersh
            Aug 19 at 7:14













            @DavidYoung Not like totally defies. It is just that I do not trust myself with it. See the postscriptum I added to my question.
            – Ignat Insarov
            Aug 19 at 8:03




            @DavidYoung Not like totally defies. It is just that I do not trust myself with it. See the postscriptum I added to my question.
            – Ignat Insarov
            Aug 19 at 8:03












            @IgnatInsarov It's possible to implement function like via @Sum @Int (<>) 3 4 which results into 3 + 4 using coerce.
            – Shersh
            Aug 19 at 8:08





            @IgnatInsarov It's possible to implement function like via @Sum @Int (<>) 3 4 which results into 3 + 4 using coerce.
            – Shersh
            Aug 19 at 8:08


















             

            draft saved


            draft discarded















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51915219%2fis-there-a-shorthand-for-operations-like-fromnewtype-f-tonewtype%23new-answer', 'question_page');

            );

            Post as a guest













































































            Comments

            Popular posts from this blog

            Long meetings (6-7 hours a day): Being “babysat” by supervisor

            Is the Concept of Multiple Fantasy Races Scientifically Flawed? [closed]

            Confectionery