Send Beginners mailing list submissions to
[email protected]
To subscribe or unsubscribe via the World Wide Web, visit
http://www.haskell.org/mailman/listinfo/beginners
or, via email, send a message with subject or body 'help' to
[email protected]
You can reach the person managing the list at
[email protected]
When replying, please edit your Subject line so it is more specific
than "Re: Contents of Beginners digest..."
Today's Topics:
1. Re: Lens (Brent Yorgey)
2. Re: exception, not in IO (Kees Bleijenberg)
3. Re: exception, not in IO (Ertugrul S?ylemez)
4. Re: exception, not in IO (Chadda? Fouch?)
5. Re: Lens (Emmanuel Surleau)
----------------------------------------------------------------------
Message: 1
Date: Mon, 15 Jul 2013 09:16:41 -0400
From: Brent Yorgey <[email protected]>
To: [email protected]
Subject: Re: [Haskell-beginners] Lens
Message-ID: <[email protected]>
Content-Type: text/plain; charset=us-ascii
Hi Emmanuel,
On Sun, Jul 14, 2013 at 10:47:16PM +0200, Emmanuel Surleau wrote:
> Hello there,
>
> The first (concrete) problem I ran into is how to update the members of a
> set with the result of an IO action. I have managed to do this with a pure
> function (prefixName) but I'm not sure of how to do this with
> promptName.
Unfortunately you cannot do this using a Set. The reason is that
modifying the contents of a Set might result in a smaller Set and that
cannot possibly satisfy the lens laws. However, if we change the Set
of dogs to be a list, we can do this using the (%%~) operator:
> ... same as before ...
>
> data Dogs = Dogs { _dogs :: [Dog] }
> deriving Show
> makeLenses ''Dogs
>
> main :: IO ()
> main = do
> ... as before ...
>
> -- change dog names by prompting the user
> doggies' <- doggies & (dogs.traverse.name) %%~ prefixName
> print doggies'
>
> return ()
But astoundingly, if you look at the implementation of (%%~), it
is... (%%~) = id! So this code also works:
doggies' <- (dogs.traverse.name) prefixName doggies
That's right, the magic solution is to just treat the
(dogs.traverse.name) lens as a *function* and apply it to prefixName!
To understand why this works we have to look a bit at the
implementation. I am not surprised that you were baffled by it
because it looks quite magical if you don't understand where it comes
from.
> type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
Instead of trying to understand why this is the definition, let's just
see what happens if we take the right-hand side and set f = IO:
(a -> IO b) -> s -> IO t
In the above example, a = b = String, and s = t = Dogs, that is,
(dogs.traverse.name) has type
Lens Dogs Dogs String String
which expands to
(String -> IO String) -> Dogs -> IO Dogs
(actually this is a lie, it is really just a Traversal and not a Lens,
but it's the same idea). So if we apply it to 'prefixName :: String
-> IO String' we get a
function of type Dogs -> IO Dogs! Nifty!
Now, to answer your specific questions:
> a) I'm not sure why the explicit forall is needed here (isn't this
> equivalent to just Functor f => ...)?
Yes, it is equivalent; the explicit forall is not strictly necessary.
The forall is there to emphasize that the 'f' does not show up on the
left-hand side: something of type Lens s t a b is a function which
works for *any* specific Functor f that a user might choose. (E.g., we
specifically chose IO in the example above). So e.g. a lens cannot do
IO operations, because then it would only work for IO and not for
other Functors. So a lens may only interact with the f via the fmap
function. (Similarly a Traversal must work with all Applicatives, and
so on.)
> b) My understanding is that a lens packs both getter and setters, but I
> don't know which is supposed to be which here...
You can think of lenses as generalizations of getters+setters, but
that is NOT how they are implemented! Nothing in the type (a -> f b)
-> s -> f t corresponds directly to getters or setters.
For understanding more about what this type means and why it
corresponds to the idea of lenses, I highly recommend the video of
Edward's presentation to the NY Haskell user's group:
http://youtu.be/cefnmjtAolY?hd=1 .
> c) Is there any kind of in-depth guide to Control.Lens somewhere? I have
> found some examples and tutorials but nothing that seemed to do more than
> scratch the surface.
You can find pretty much everything there is on lens here:
http://lens.github.io/
-Brent
------------------------------
Message: 2
Date: Mon, 15 Jul 2013 16:01:04 +0200
From: "Kees Bleijenberg" <[email protected]>
To: "'The Haskell-Beginners Mailing List - Discussion of primarily
beginner-level topics related to Haskell'" <[email protected]>
Subject: Re: [Haskell-beginners] exception, not in IO
Message-ID: <000901ce8163$bed15ba0$3c7412e0$@[email protected]>
Content-Type: text/plain; charset="us-ascii"
Ertugrul,
I don't understand all of it. This is what I've done
{-# LANGUAGE OverloadedStrings,DeriveDataTypeable #-}
module Test()
where
import Text.JSON
import Text.JSON.Generic
import Control.Exception
import Control.Monad
data Glass = Glass { a:: String,
b:: String} deriving (Show,Typeable,Eq,Data,Read)
data MyError = UnknownError deriving Show
glassDecode :: String -> (Either MyError Glass)
glassDecode s = Right ((decodeJSON s) :: Glass)
glassEncode :: Glass -> Either MyError String
glassEncode g = return $ encodeJSON g
convert :: String -> Either MyError String
convert = glassEncode <=< glassDecode
test = convert "{\"a\":\"blah\",\"b\":\"blahb\"}"
test2 = convert "{\"\":\"blah\",\"b\":\"blahb\"}"
If I call test I get back Right <theInputString>
If I call test2 (invalid string) I get Right "{*** Exception: fromJSON:
field does not exist a"
The good news is that the program doesn't halt any more.
How do I transform the last Right to a Left (without changing the source in
Text.JSON.Generic) if there is a error? The code in Text.JSON.Generic calls
error "xxx" if something is wrong.
Kees
.....
IO is just one of the many monads with exception support. For your case,
since JSON parsing is a pure process, you would want to use a pure exception
monad like `Maybe` or `Either MyError`:
data MyError
= InvalidDateField
| {- ... -}
| UnknownError
There is nothing wrong with using regular exception types, if you wish, in
which case you might use `Either SomeException`. Then separate
concerns:
decode :: String -> Either MyError Glass
encode :: Glass -> String
Finally the conversion function is as simple as:
convert :: String -> Either MyError String
convert = fmap encode . decode
If `encode` can fail as well and exceptions are regular Haskell
exceptions:
import Control.Exception
import Control.Monad
decode :: String -> Either SomeException Glass
encode :: Glass -> Either SomeException String
convert :: String -> Either SomeException String
convert = encode <=< decode
------------------------------
Message: 3
Date: Mon, 15 Jul 2013 13:47:18 +0200
From: Ertugrul S?ylemez <[email protected]>
To: [email protected]
Subject: Re: [Haskell-beginners] exception, not in IO
Message-ID: <[email protected]>
Content-Type: text/plain; charset="us-ascii"
Lyndon Maydwell <[email protected]> wrote:
> I agree that it seems uncomfortable for a seemingly pure function to
> require handling exceptions in the IO monad, but if the code in
> question really does have the capability of raising IO exceptions,
> then I think it is reasonable to consider it as impure and belonging
> in the IO monad and that it probably should not have been given a pure
> type signature to start with.
No, pure computations can throw Haskell exceptions just fine, and the
exceptions can be caught and handled without IO. Using Either
SomeException instead of IO is the recommended way to do it.
> I'd double check to see if the function in question has the capability
> of raising an exception in a more pure monad as per Kees's
> suggestion. The other option is to perform the actions I've suggested
> and then wrap the whole lot in unsafePerformIO to make it pure again
> but with a new signature of (a -> Either YourException b), however
> that just makes me feel even more queazy...
That's about the worst solution you could consider. It's not a valid
use case for unsafePerformIO. You can just use Either right away. I
don't understand why you insist on IO and use hazardously unsafe
constructs to satisfy the insistence.
There is a natural transformation from Either SomeException to IO:
liftEither :: (Exception e) => Either e a -> IO a
liftEither = either throwIO return
Greets,
Ertugrul
--
Not to be or to be and (not to be or to be and (not to be or to be and
(not to be or to be and ... that is the list monad.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL:
<http://www.haskell.org/pipermail/beginners/attachments/20130715/23e65944/attachment-0001.sig>
------------------------------
Message: 4
Date: Mon, 15 Jul 2013 17:39:47 +0200
From: Chadda? Fouch? <[email protected]>
To: The Haskell-Beginners Mailing List - Discussion of primarily
beginner-level topics related to Haskell <[email protected]>
Subject: Re: [Haskell-beginners] exception, not in IO
Message-ID:
<canfjzrzd61oyvopcjrju_cnbhvw4rvpzqccsqt4rptqqs0k...@mail.gmail.com>
Content-Type: text/plain; charset=UTF-8
On Mon, Jul 15, 2013 at 1:47 PM, Ertugrul S?ylemez <[email protected]> wrote:
>
>> I'd double check to see if the function in question has the capability
>> of raising an exception in a more pure monad as per Kees's
>> suggestion. The other option is to perform the actions I've suggested
>> and then wrap the whole lot in unsafePerformIO to make it pure again
>> but with a new signature of (a -> Either YourException b), however
>> that just makes me feel even more queazy...
>
> That's about the worst solution you could consider. It's not a valid
> use case for unsafePerformIO. You can just use Either right away. I
> don't understand why you insist on IO and use hazardously unsafe
> constructs to satisfy the insistence.
I think there's a point you didn't catch on : the decode function is
provided as is by a library and isn't written by the original poster,
if that calls error, the decode function can't be sanely used in a
pure solution to catch incorrect input, except by some monstrosity as
proposed by some (I think the spoon library encapsulated this horror
neatly).
Of course, there's a thing that haven't been considered, the source
code is available, so let's look at this function :
-- |Decode a string as a value.
decodeJSON :: (Data a) => String -> a
decodeJSON s =
case runGetJSON readJSValue s of
Left msg -> error msg
Right j ->
case fromJSON j of
Error msg -> error msg
Ok x -> x
We discover that this function is pretty simple in fact and combine
some pure code that does provide pure exceptions, so defining a new
function may bring a solution :
-- |Decode a string as a value.
tryDecodingJSON :: (Data a) => String -> Result a
tryDecodingJSON s =
case runGetJSON readJSValue s of
Left msg -> Error msg
Right j -> fromJSON j
You may need to import Text.JSON.String for runGetJSON.
--
Jeda?
------------------------------
Message: 5
Date: Tue, 16 Jul 2013 07:09:43 +0200
From: Emmanuel Surleau <[email protected]>
To: The Haskell-Beginners Mailing List - Discussion of primarily
beginner-level topics related to Haskell <[email protected]>
Subject: Re: [Haskell-beginners] Lens
Message-ID:
<CADd2AG7fZrOODeftWD=tbrrkvxn4e+vygm+uabrecfufgkl...@mail.gmail.com>
Content-Type: text/plain; charset="iso-8859-1"
Hi Brent,
Thanks for the link to the video. I watched the first half yesterday, and
it's definitely content rich. But the overall design of the library is
starting to make sense now. The reason it doesn't work for Sets (and I
assume any similar structure like hashmaps) is because, depending on the
implementation of Eq on the item the Set is parametrized on and the field
being modified, you could accidentally remove another item - and this would
be a BAD THING, since applying a setter on a traversal is basically
"fmap++" and fmap doesn't let you remove things. Is this correct?
Also, thanks for the traversal example with the list, that's certainly
going to be useful.
Cheers,
Emm
On Mon, Jul 15, 2013 at 3:16 PM, Brent Yorgey <[email protected]>wrote:
> Hi Emmanuel,
>
> On Sun, Jul 14, 2013 at 10:47:16PM +0200, Emmanuel Surleau wrote:
> > Hello there,
> >
> > The first (concrete) problem I ran into is how to update the members of a
> > set with the result of an IO action. I have managed to do this with a
> pure
> > function (prefixName) but I'm not sure of how to do this with
> > promptName.
>
> Unfortunately you cannot do this using a Set. The reason is that
> modifying the contents of a Set might result in a smaller Set and that
> cannot possibly satisfy the lens laws. However, if we change the Set
> of dogs to be a list, we can do this using the (%%~) operator:
>
> > ... same as before ...
> >
> > data Dogs = Dogs { _dogs :: [Dog] }
> > deriving Show
> > makeLenses ''Dogs
> >
> > main :: IO ()
> > main = do
> > ... as before ...
> >
> > -- change dog names by prompting the user
> > doggies' <- doggies & (dogs.traverse.name) %%~ prefixName
> > print doggies'
> >
> > return ()
>
> But astoundingly, if you look at the implementation of (%%~), it
> is... (%%~) = id! So this code also works:
>
> doggies' <- (dogs.traverse.name) prefixName doggies
>
> That's right, the magic solution is to just treat the
> (dogs.traverse.name) lens as a *function* and apply it to prefixName!
>
> To understand why this works we have to look a bit at the
> implementation. I am not surprised that you were baffled by it
> because it looks quite magical if you don't understand where it comes
> from.
>
> > type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
>
> Instead of trying to understand why this is the definition, let's just
> see what happens if we take the right-hand side and set f = IO:
>
> (a -> IO b) -> s -> IO t
>
> In the above example, a = b = String, and s = t = Dogs, that is,
> (dogs.traverse.name) has type
>
> Lens Dogs Dogs String String
>
> which expands to
>
> (String -> IO String) -> Dogs -> IO Dogs
>
> (actually this is a lie, it is really just a Traversal and not a Lens,
> but it's the same idea). So if we apply it to 'prefixName :: String
> -> IO String' we get a
> function of type Dogs -> IO Dogs! Nifty!
>
> Now, to answer your specific questions:
>
> > a) I'm not sure why the explicit forall is needed here (isn't this
> > equivalent to just Functor f => ...)?
>
> Yes, it is equivalent; the explicit forall is not strictly necessary.
> The forall is there to emphasize that the 'f' does not show up on the
> left-hand side: something of type Lens s t a b is a function which
> works for *any* specific Functor f that a user might choose. (E.g., we
> specifically chose IO in the example above). So e.g. a lens cannot do
> IO operations, because then it would only work for IO and not for
> other Functors. So a lens may only interact with the f via the fmap
> function. (Similarly a Traversal must work with all Applicatives, and
> so on.)
>
> > b) My understanding is that a lens packs both getter and setters, but I
> > don't know which is supposed to be which here...
>
> You can think of lenses as generalizations of getters+setters, but
> that is NOT how they are implemented! Nothing in the type (a -> f b)
> -> s -> f t corresponds directly to getters or setters.
>
> For understanding more about what this type means and why it
> corresponds to the idea of lenses, I highly recommend the video of
> Edward's presentation to the NY Haskell user's group:
> http://youtu.be/cefnmjtAolY?hd=1 .
>
> > c) Is there any kind of in-depth guide to Control.Lens somewhere? I have
> > found some examples and tutorials but nothing that seemed to do more than
> > scratch the surface.
>
> You can find pretty much everything there is on lens here:
>
> http://lens.github.io/
>
> -Brent
>
> _______________________________________________
> Beginners mailing list
> [email protected]
> http://www.haskell.org/mailman/listinfo/beginners
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://www.haskell.org/pipermail/beginners/attachments/20130716/c4974766/attachment.html>
------------------------------
Subject: Digest Footer
_______________________________________________
Beginners mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/beginners
------------------------------
End of Beginners Digest, Vol 61, Issue 20
*****************************************