[Haskell-cafe] Handling NULL value , was :trace output statements

Damien Mattei damien.mattei at gmail.com
Thu Dec 27 00:03:10 UTC 2018


finally, late in night i got myself a working solution :-) :
getBD2 :: Connection -> String -> IO (Maybe Float)
getBD2 conn name = do
            let qry_head_BD_Sidonie = "select `N° BD` from Coordonnées
where Nom = ?" :: Query
            (bd_rows :: [Only (Maybe Text)]) <- query conn
qry_head_BD_Sidonie (Only (name::String))
            let noBDtxt = fromOnly (Prelude.head bd_rows) :: Maybe Text
            case noBDtxt of
                 Nothing -> return Nothing
                 Just noBDtxt -> do
                                   putStrLn "getBD2"
                                   let noBDstr = Text.unpack noBDtxt ::
String
                                   let noBDfp = readMaybe noBDstr :: Maybe
Float
                                   return noBDfp

note that i had to change read in readMaybe due to Exception: Prelude.read:
no parse,solved here (i do not really understand why) :
https://stackoverflow.com/questions/27947925/haskell-prelude-read-no-parse-string

note also in the last lines i infer a Maybe Float from a NOT maybe String ,
but the compiler did not notice it....

in my main function i call and display like that:(2 solution due to a Just
appearing in terminal...)

 noBD <- getBD2 conn name
    putStrLn ("noBD = " ++ (show noBD))
    putStrLn $ "noBD = " ++ maybe "NULL" show noBD

display like this:

getBD2
noBD = Just (-4.3982)
noBD = -4.3982

but if someone has a more elegant or concise solution ? ....

my opinion: Haskell is weird... :-)


On Wed, Dec 26, 2018 at 11:24 PM Damien Mattei <damien.mattei at gmail.com>
wrote:

> i'm learning fmap, but for now i want to convert the previous function:
>
> getBD :: Connection -> String -> IO Float
> getBD conn name = do
>             let qry_head_BD_Sidonie = "select `N° BD` from Coordonnées
> where Nom = ?" :: Query
>             (bd_rows :: [Only Text]) <- query conn qry_head_BD_Sidonie
> (Only (name::String))
>             let noBDtxt = fromOnly (Prelude.head bd_rows) :: Text
>             let noBDstr = Text.unpack noBDtxt :: String
>             let noBDfp = read $ noBDstr :: Float
>             return noBDfp
>
> that works but fails in case of NULL in database,
> i want the code to works also with NULL, detecting them with Nothing, and
> short circuit them with Maybe or something else, so i change the function
> definition to this type and here is the whole code:
>
> getBD2 :: Connection -> String -> IO (Maybe Float)
> getBD2 conn name = do
>             let qry_head_BD_Sidonie = "select `N° BD` from Coordonnées
> where Nom = ?" :: Query
>             (bd_rows :: [Only (Maybe Text)]) <- query conn
> qry_head_BD_Sidonie (Only (name::String))
>             let noBDtxt = if (isNothing (fromOnly (Prelude.head bd_rows)))
>                           then (return Nothing)
>                           else (fromOnly (Prelude.head bd_rows) :: Maybe
> Text)
>             let noBDstr = Text.unpack noBDtxt :: Maybe String
>             let noBDfp = read $ noBDstr :: Maybe Float
>             return noBDfp
>
> unfortunately it fails to compile, something is wrong here:
>
> Prelude Data.Maybe> :load UpdateSidonie
> [1 of 1] Compiling Main             ( UpdateSidonie.hs, interpreted )
>
> UpdateSidonie.hs:68:33: error:
>     • Couldn't match type ‘Text’ with ‘Maybe a’
>       Expected type: Maybe (Maybe a)
>         Actual type: Maybe Text
>     • In the expression:
>         (fromOnly (Prelude.head bd_rows) :: Maybe Text)
>       In the expression:
>         if (isNothing (fromOnly (Prelude.head bd_rows))) then
>             (return Nothing)
>         else
>             (fromOnly (Prelude.head bd_rows) :: Maybe Text)
>       In an equation for ‘noBDtxt’:
>           noBDtxt
>             = if (isNothing (fromOnly (Prelude.head bd_rows))) then
>                   (return Nothing)
>               else
>                   (fromOnly (Prelude.head bd_rows) :: Maybe Text)
>     • Relevant bindings include
>         noBDtxt :: Maybe (Maybe a) (bound at UpdateSidonie.hs:66:17)
>    |
> 68 |                           else (fromOnly (Prelude.head bd_rows) ::
> Maybe Text)
>    |
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>
> UpdateSidonie.hs:69:27: error:
>     • Couldn't match type ‘[Char]’ with ‘Maybe String’
>       Expected type: Maybe String
>         Actual type: String
>     • In the expression: unpack noBDtxt :: Maybe String
>       In an equation for ‘noBDstr’:
>           noBDstr = unpack noBDtxt :: Maybe String
>       In the expression:
>         do let qry_head_BD_Sidonie = ...
>            (bd_rows :: [Only (Maybe Text)]) <- query
>                                                  conn qry_head_BD_Sidonie
> (Only (name :: String))
>            let noBDtxt = ...
>            let noBDstr = ...
>            ....
>    |
> 69 |             let noBDstr = Text.unpack noBDtxt :: Maybe String
>    |                           ^^^^^^^^^^^^^^^^^^^
>
> UpdateSidonie.hs:69:39: error:
>     • Couldn't match expected type ‘Text’
>                   with actual type ‘Maybe (Maybe a0)’
>     • In the first argument of ‘unpack’, namely ‘noBDtxt’
>       In the expression: unpack noBDtxt :: Maybe String
>       In an equation for ‘noBDstr’:
>           noBDstr = unpack noBDtxt :: Maybe String
>    |
> 69 |             let noBDstr = Text.unpack noBDtxt :: Maybe String
>    |                                       ^^^^^^^
>
> UpdateSidonie.hs:70:33: error:
>     • Couldn't match type ‘Maybe String’ with ‘[Char]’
>       Expected type: String
>         Actual type: Maybe String
>     • In the second argument of ‘($)’, namely ‘noBDstr’
>       In the expression: read $ noBDstr :: Maybe Float
>       In an equation for ‘noBDfp’: noBDfp = read $ noBDstr :: Maybe Float
>    |
> 70 |             let noBDfp = read $ noBDstr :: Maybe Float
>    |                                 ^^^^^^^
> Failed, no modules loaded.
>
> what is the solution? it will help me in my project and to understand
> Haskell way of handling null objects.
>
> Damien
>
>
> On Tue, Dec 25, 2018 at 11:18 PM Ian Denhardt <ian at zenhack.net> wrote:
>
>>
>> (Adding the list back to Cc; you forgot to hit reply all)
>>
>> Quoting Damien Mattei (2018-12-25 16:57:04)
>>
>> > i get in trouble understanding what fmap was doing,
>>
>> fmap (for IO) just applies a function to the result of the action, so:
>>
>>     fmap f action
>>
>> is equivalent to:
>>
>>     do
>>         result <- action
>>         return (f result)
>>
>> > and why the same thing to do in main and in a function had so
>> > different implementations...
>>
>> I suspect the modified version of the program has some differences that
>> aren't strictly necessary just to factor out the relevant bits into
>> their own definition; this *shouldn't* be major surgery. Hard for me to
>> point out without having the two full examples handy.
>>
>> Quoting Damien Mattei (2018-12-25 16:57:04)
>> >    yes, i understand with your python example, it's like read on a web
>> >    page, IO are not a "cake" but IO are "a recipe for the cake"...
>> >    functions are pure in haskell, so they can not have side effects...
>> >    what put me some trouble is an answer 2 weeks ago, someone gave me
>> >    hints that lead to this solution for getDB:
>> >    getBD :: Connection -> String -> IO Float
>> >    getBD conn name = trace "Entering getBD" noBDfp
>> >    �  where qry_head_BD_Sidonie = "select `N° BD` from Coordonnées where
>> >    Nom = ?" :: Query
>> >    � � � � � � �  bd_rows :: IO [Only Text]
>> >    � � � � � � �  bd_rows = query conn qry_head_BD_Sidonie (Only
>> >    (name::String))
>> >    � � � � � � �  noBDtxt :: IO Text
>> >    � � � � � � �  noBDtxt = trace "assigning noBDtxt" (fmap (fromOnly .
>> >    Prelude.head) bd_rows)
>> >    � � � � � � �  noBDstr :: IO String
>> >    � � � � � � �  noBDstr = trace "assigning noBDstr" (fmap Text.unpack
>> >    noBDtxt)
>> >    � � � � � � �  noBDfp :: IO Float
>> >    � � � � � � �  noBDfp = fmap read noBDstr
>> >    which was code different from the code in main,i get in trouble
>> >    understanding what fmap was doing , and why the same thing to do in
>> >    main and in a function had so different implementations...
>> >
>> >    On Tue, Dec 25, 2018 at 10:19 PM Ian Denhardt <[1]ian at zenhack.net>
>> >    wrote:
>> >
>> >      The correct type annotation for getDB3 should be:
>> >      �  �  getDB3 :: Connection -> String -> IO Float
>> >      Note the IO at the end. Functions in Haskell are just pure
>> >      computation;
>> >      they can't have side effects -- so a function returning a Float
>> >      can't
>> >      possibly talk to a database.
>> >      Instead, The type `IO a` represents a description of an action to
>> >      perform.�  It's still just a value -- calling getDB3 doesn't *do*
>> >      anything. You can stitch these together using do-notation or
>> >      functions
>> >      like >>=, and when the program is run the action defined by 'main'
>> >      is
>> >      performed.
>> >      ---
>> >      An analogy: you could imagine instead of IO we could give up, and
>> >      write
>> >      code that computes a program in some other (imperative) programming
>> >      language) that we then hand off to an interpreter. For example, we
>> >      could
>> >      compute a python program that counts from 1 to 99 like so:
>> >      �  �  printNum :: Int -> String
>> >      �  �  printNum n = "print('" ++ show n ++ "')\n"
>> >      �  �  pythonProgram = concatMap printNum [1..99]
>> >      So we've defined a variable fullProgram that is a string with the
>> >      source
>> >      code to a python program like:
>> >      �  �  print('1')
>> >      �  �  print('2')
>> >      �  �  print('3')
>> >      �  �  ...
>> >      �  �  print('99')
>> >      ..but we haven't actually run it. To do that we'd have to pass the
>> >      string off to the python interpreter.
>> >      This is a good way to think about what IO is -- main is like our
>> >      pythonProgram above, in that it is a description of actions to
>> >      perform,
>> >      but *evaluating* it doesn't have any side effects -- it just
>> >      computes
>> >      the description. When you run a Haskell program, this is like
>> taking
>> >      the description defined by Main and passing it off to an
>> >      interpreter.
>> >      ---
>> >      So your definition of getDB3 is a description of actions to perform
>> >      to
>> >      get a float from the database, but your type declaration says it's
>> a
>> >      function that computes a float (without having to perform any
>> >      "actions").
>> >      This is a critical distinction that exists in Haskell but not most
>> >      other
>> >      languages.
>> >      Hope this helps,
>> >      -Ian
>> >      Quoting Damien Mattei (2018-12-25 15:07:35)
>> >      >�  �  yes i use do notation, but for example i have code that
>> works
>> >      in main
>> >      >�  �  and not in a function!
>> >      >�  �  i print the code perheaps someone could help me:
>> >      >�  �  first the function, so you have the import list too:
>> >      >�  �  import Database.MySQL.Simple
>> >      >�  �  import Database.MySQL.Simple.QueryResults
>> >      >�  �  import Database.MySQL.Simple.Result
>> >      >�  �  import Database.MySQL.Simple.QueryParams
>> >      >�  �  import Database.MySQL.Simple.Param
>> >      >�  �  import Control.Monad
>> >      >�  �  import Data.Text as Text
>> >      >�  �  --import Data.Int as Int
>> >      >�  �  --import Data.List
>> >      >�  �  import Debug.Trace
>> >      >�  �  import Data.Maybe as Maybe
>> >      >�  �  -- this function will return th N°BD from Sidonie for a
>> >      given name
>> >      >�  �  -- note: names have been standardized between Sidonie and
>> WDS
>> >      >�  �  getBD3 :: Connection -> String -> Float
>> >      >�  �  getBD3 conn name = do
>> >      >�  �  � � � � � � � � � � ��  let
>> >      qry_head_BD_Sidonie = "select `N° BD` from
>> >      >�  �  Coordonnées where Nom = ?" :: Query
>> >      >�  �  � � � � � � � � � � ��  (bd_rows ::
>> >      [Only Text]) <- query conn
>> >      >�  �  qry_head_BD_Sidonie (Only (name::String))
>> >      >�  �  � � � � � � � � � � ��  let noBDtxt =
>> >      fromOnly (Prelude.head bd_rows) ::
>> >      >�  �  Text
>> >      >�  �  � � � � � � � � � � ��  let noBDstr =
>> >      Text.unpack noBDtxt :: String
>> >      >�  �  � � � � � � � � � � ��  let noBDfp =
>> >      read $ noBDstr :: Float
>> >      >�  �  � � � � � � � � � � ��  return noBDfp
>> >      >�  �  with this function i have this error:
>> >      >�  �  Prelude> :load UpdateSidonie
>> >      >�  �  [1 of 1] Compiling Main� � � � � � � � �
>> >      � � ��  ( UpdateSidonie.hs,
>> >      >�  �  interpreted )
>> >      >�  �  UpdateSidonie.hs:54:13: error:
>> >      >�  �  � � ��  � Couldn't match expected type �Float�
>> >      with actual type �IO
>> >      >�  �  Float�
>> >      >�  �  � � ��  � In a stmt of a 'do' block:
>> >      >�  �  � � � � � � ��  (bd_rows :: [Only Text]) <-
>> >      query
>> >      >�  �  � � � � � � � � � � � � � � �
>> >      � � � � � � � � � � � � � � � � �
>> >      � � � �
>> >      >�  �  � ��  conn qry_head_BD_Sidonie (Only (name :: String))
>> >      >�  �  � � � � ��  In the expression:
>> >      >�  �  � � � � � � ��  do let qry_head_BD_Sidonie =
>> >      ...
>> >      >�  �  � � � � � � � � � ��  (bd_rows :: [Only
>> >      Text]) <- query
>> >      >�  �  � � � � � � � � � � � � � � �
>> >      � � � � � � � � � � � � � � � � �
>> >      � � � �
>> >      >�  �  � � � � ��  conn qry_head_BD_Sidonie (Only (name ::
>> >      String))
>> >      >�  �  � � � � � � � � � ��  let noBDtxt = ...
>> >      >�  �  � � � � � � � � � ��  let noBDstr = ...
>> >      >�  �  � � � � � � � � � ��  ....
>> >      >�  �  � � � � ��  In an equation for �getBD3�:
>> >      >�  �  � � � � � � � � ��  getBD3 conn name
>> >      >�  �  � � � � � � � � � � ��  = do let
>> >      qry_head_BD_Sidonie = ...
>> >      >�  �  � � � � � � � � � � � � � � �
>> >      ��  (bd_rows :: [Only Text]) <- query
>> >      >�  �  � � � � � � � � � � � � � � �
>> >      � � � � � � � � � � � � � � � � �
>> >      � � � �
>> >      >�  �  � � � � � � � � � � ��  conn
>> >      qry_head_BD_Sidonie (Only (name :: String))
>> >      >�  �  � � � � � � � � � � � � � � �
>> >      ��  let noBDtxt = ...
>> >      >�  �  � � � � � � � � � � � � � � �
>> >      ��  ....
>> >      >�  �  � ��  |
>> >      >�  �  54 |� � � � � � � � � � � ��
>> >      (bd_rows :: [Only Text]) <- query conn
>> >      >�  �  qry_head_BD_Sidonie (Only (name::String))
>> >      >�  �  � ��  |� � � � � � � � � � � �
>> >      >�  �
>> >
>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>> >      ^^^
>> >      >�  �  ^^^^^^^^^
>> >      >�  �  Failed, no modules loaded.
>> >      >�  �  i do not understand the error complaining that i return an
>> IO
>> >      >�  �  float,because i'm sure it's a float in noBDfp
>> >      >�  �  if i put the same lines of code in the main it works !!! :
>> >      >�  �  main :: IO ()
>> >      >�  �  main =
>> >      >�  �  ��  do
>> >      >�  �  � � ��  conn <- connect defaultConnectInfo
>> >      >�  �  � � � � ��  { connectHost = "moita",
>> >      >�  �  � � � � � � ��  connectUser = "mattei",
>> >      >�  �  � � � � � � ��  connectPassword = "sidonie2",
>> >      >�  �  � � � � � � ��  connectDatabase = "sidonie" }
>> >      >�  �  � let qry_head_BD_Sidonie = "select `N° BD` from
>> >      Coordonnées where
>> >      >�  �  Nom = ?" :: Query
>> >      >�  �  � (bd_rows :: [Only Text]) <- query conn
>> >      qry_head_BD_Sidonie (Only
>> >      >�  �  (name::String))
>> >      >�  �  putStr "bd_rows ="
>> >      >�  �  putStrLn $ show bd_rows
>> >      >�  �  � � ��  let noBDtxt = fromOnly (Prelude.head bd_rows)
>> >      :: Text
>> >      >�  �  � � ��  let noBDstr = Text.unpack noBDtxt :: String
>> >      >�  �  � � ��  let noBDfp = read $ noBDstr :: Float
>> >      >�  �  � � ��  putStr "noBDfp ="
>> >      >�  �  � � ��  (putStrLn (show noBDfp))
>> >      >�  �  � close conn
>> >      >�  �  it works i have output like this:
>> >      >�  �  *Main> main
>> >      >�  �  bd_rows =[Only {fromOnly = "-04.3982"}]
>> >      >�  �  noBDtxt ="-04.3982"
>> >      >�  �  noBDfp =-4.3982
>> >      >�  �  noBDfp + 1 = -3.3982
>> >      >�  �  i'm well getting a float in noBDfp , i even can add 1 to it
>> >      :-) ( cool
>> >      >�  �  haskell...)
>> >      >�  �  but i'm just wanting to that in the function getDB3 but it
>> >      does not
>> >      >�  �  compile...
>> >      >�  �  ??????
>> >      >�  �  Damien
>> >      >
>> >      >�  �  On Sun, Dec 23, 2018 at 4:54 PM Tom Ellis
>> >      >�  �  <[1][2]tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk> wrote:
>> >      >
>> >      >�  �  �  I think forgetting about monads and just using
>> do-notation
>> >      will help
>> >      >�  �  �  you.
>> >      >�  �  �  On Sun, Dec 23, 2018 at 04:44:57PM +0100, Damien Mattei
>> >      wrote:
>> >      >�  �  �  > i think learning Monads from scratch again will help me
>> >      >�  �  �  >
>> >      >�  �  �  > On Sun, Dec 23, 2018 at 4:11 PM Tom Ellis <
>> >      >�  �  �  > [2][3]tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk>
>> >      wrote:
>> >      >�  �  �  >
>> >      >�  �  �  > > Yes, exactly!
>> >      >�  �  �  > >
>> >      >�  �  �  > > On Sun, Dec 23, 2018 at 02:08:57PM +0100, Damien
>> >      Mattei wrote:
>> >      >�  �  �  > > > lazyness....?
>> >      >�  �  �  > > >
>> >      >�  �  �  > > > On Sun, Dec 23, 2018 at 8:40 AM Tom Ellis <
>> >      >�  �  �  > > > [3][4]tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk>
>> >      wrote:
>> >      >�  �  �  > > >
>> >      >�  �  �  > > > > On Sat, Dec 22, 2018 at 09:52:18AM +0100, Damien
>> >      Mattei
>> >      >�  �  �  wrote:
>> >      >�  �  �  > > > > > i have inserted trace statement that output
>> >      variable
>> >      >�  �  �  > > > > > ... i have strange behavior of output:
>> >      >�  �  �  > > > >
>> >      >�  �  �  > > > > Let's take a simpler example.��  Do you
>> >      understand why the
>> >      >�  �  �  trace
>> >      >�  �  �  > > statments
>> >      >�  �  �  > > > > from this small program appear in the order that
>> >      they do?�
>> >      >�  �  �  (And for
>> >      >�  �  �  > > what
>> >      >�  �  �  > > > > it's worth I really think you'll be better off
>> >      writing
>> >      >�  �  �  programs using
>> >      >�  �  �  > > do
>> >      >�  �  �  > > > > notation).
>> >      >�  �  �  > > > >
>> >      >�  �  �  > > > >
>> >      >�  �  �  > > > > % cat test.hs
>> >      >�  �  �  > > > > import Debug.Trace
>> >      >�  �  �  > > > >
>> >      >�  �  �  > > > > result =
>> >      >�  �  �  > > > >��  � let a = trace "evaluating a" 2
>> >      >�  �  �  > > > >��  ��  ��  � b = trace "evaluating b" 10
>> >      >�  �  �  > > > >��  ��  ��  � c = trace "evaluating c" (a +
>> >      b)
>> >      >�  �  �  > > > >��  � in c
>> >      >�  �  �  > > > > ~% ghci -e result test.hs
>> >      >�  �  �  > > > > evaluating c
>> >      >�  �  �  > > > > evaluating b
>> >      >�  �  �  > > > > evaluating a
>> >      >�  �  �  > > > > 12
>> >      >�  �  �  > > _______________________________________________
>> >      >�  �  �  > > Haskell-Cafe mailing list
>> >      >�  �  �  > > To (un)subscribe, modify options or view archives go
>> >      to:
>> >      >�  �  �  > >
>> >      [4][5]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >      >�  �  �  > > Only members subscribed via the mailman list are
>> >      allowed to
>> >      >�  �  �  post.
>> >      >�  �  �  > _______________________________________________
>> >      >�  �  �  > Haskell-Cafe mailing list
>> >      >�  �  �  > To (un)subscribe, modify options or view archives go
>> to:
>> >      >�  �  �  >
>> >      [5][6]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >      >�  �  �  > Only members subscribed via the mailman list are
>> allowed
>> >      to post.
>> >      >�  �  �  _______________________________________________
>> >      >�  �  �  Haskell-Cafe mailing list
>> >      >�  �  �  To (un)subscribe, modify options or view archives go to:
>> >      >�  �  �
>> >      [6][7]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >      >�  �  �  Only members subscribed via the mailman list are allowed
>> >      to post.
>> >      >
>> >      > Verweise
>> >      >
>> >      >�  �  1. mailto:[8]tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
>> >      >�  �  2. mailto:[9]tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
>> >      >�  �  3. mailto:[10]tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
>> >      >�  �  4.
>> >      [11]http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >      >�  �  5.
>> >      [12]http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >      >�  �  6.
>> >      [13]http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >
>> > Verweise
>> >
>> >    1. mailto:ian at zenhack.net
>> >    2. mailto:tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
>> >    3. mailto:tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
>> >    4. mailto:tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
>> >    5. http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >    6. http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >    7. http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >    8. mailto:tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
>> >    9. mailto:tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
>> >   10. mailto:tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
>> >   11. http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >   12. http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> >   13. http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20181227/1cce3ee4/attachment-0001.html>


More information about the Haskell-Cafe mailing list