From pietro.gra at hotmail.it Thu Feb 9 14:08:05 2023 From: pietro.gra at hotmail.it (Pietro Grandinetti) Date: Thu, 9 Feb 2023 14:08:05 +0000 Subject: [Haskell-beginners] Generic use of queries postgresql-simple Message-ID: Hello-- I use postgresql-simple and for selecting rows from one table I have the following function. runMyQuery :: Connection -> String -- query with '?' params to bind -> Int -- first param -> Int -- second param -> IO [MyTable1] runMyQuery conn dbQuery param1 param2 = query conn dbQuery $ (param2, param2) data MyTable1 = MyTable1 {col1 :: Int, col2 :: Int, col3 :: String} instance FromRow MyTable1 where fromRow = MyTable1 <$> field <*> field <*> field <*> field There are some things I'd like to improve: 1. I will have to write "MyTable1", "MyTable2", ... datatypes, one for each table, but would like to have only one function runMyQuery where the return type is a generic IO [MyModel]. My problem is that I don't know how to write it for tables with different number of columns and different SQL types. 2. The 2nd to the penultimate arguments of runMyQuery are the arguments to bind into the SQL string query. This means that I should write several different version of "runMyQuery" each with a different number of arguments, but would like to have only one. Can you recommend some approaches? Thanks. -------------- next part -------------- An HTML attachment was scrubbed... URL: From pedroborg at gmail.com Fri Feb 17 16:04:28 2023 From: pedroborg at gmail.com (Pedro B.) Date: Fri, 17 Feb 2023 12:04:28 -0400 Subject: [Haskell-beginners] Attoparsec.ByteString.Char8 or Attoparsec.ByteString for diff output? Message-ID: <2dfc932c-969f-2dd7-c8b7-26ffcba1eb70@gMail.com> Dear Listers, I am developing a program to parse dif output taken from stdin (as in diff file1 file2 | myApp) or from a file. I am reading the input as ByteString in either case and I am parsing it Attoparsec. My question is, Should I use Data.Attoparsec.ByteString.Char8 or Data.Attoparsec.ByteString? So far, I've been using Data.Attoparsec.ByteString.Char8 and it works for my sample files, which are in utf8 or, latin1, or the default Windows encoding. What do you suggest? Regards, Pedro Borges From pedroborg at gmail.com Mon Feb 20 14:39:06 2023 From: pedroborg at gmail.com (Pedro B.) Date: Mon, 20 Feb 2023 10:39:06 -0400 Subject: [Haskell-beginners] Attoparsec.ByteString.Char8 or Attoparsec.ByteString for diff output? In-Reply-To: <2dfc932c-969f-2dd7-c8b7-26ffcba1eb70@gMail.com> References: <2dfc932c-969f-2dd7-c8b7-26ffcba1eb70@gMail.com> Message-ID: <5bbd9ac4-f9b7-e443-405d-f5e47b734fde@gMail.com> I send this question to the haskell-cafe list and I've got a couple of answers there. Pedro Borges El 17/2/2023 a las 12:04 p. m., Pedro B. escribió: > Dear Listers, > > I am developing a program to parse dif output taken from stdin (as in > diff file1 file2 | myApp) or from a file. I am reading  the input as > ByteString in either case and I am parsing it Attoparsec. My question > is, Should I use Data.Attoparsec.ByteString.Char8  or > Data.Attoparsec.ByteString? > > So far, I've been  using Data.Attoparsec.ByteString.Char8  and it works > for my sample files, which are in utf8 or, latin1, or the default > Windows encoding. > > What do you suggest? > > Regards, > > Pedro Borges From ky3 at atamo.com Mon Feb 20 20:04:45 2023 From: ky3 at atamo.com (Kim-Ee Yeoh) Date: Tue, 21 Feb 2023 03:04:45 +0700 Subject: [Haskell-beginners] Attoparsec.ByteString.Char8 or Attoparsec.ByteString for diff output? In-Reply-To: <5bbd9ac4-f9b7-e443-405d-f5e47b734fde@gMail.com> References: <2dfc932c-969f-2dd7-c8b7-26ffcba1eb70@gMail.com> <5bbd9ac4-f9b7-e443-405d-f5e47b734fde@gMail.com> Message-ID: Hi Pedro, I noticed the responses on the cafe list. Hope they helped! On Mon, Feb 20, 2023 at 9:39 PM Pedro B. wrote: > I send this question to the haskell-cafe list and I've got a couple of > answers there. > > Pedro Borges > > > El 17/2/2023 a las 12:04 p. m., Pedro B. escribió: > > Dear Listers, > > > > I am developing a program to parse dif output taken from stdin (as in > > diff file1 file2 | myApp) or from a file. I am reading the input as > > ByteString in either case and I am parsing it Attoparsec. My question > > is, Should I use Data.Attoparsec.ByteString.Char8 or > > Data.Attoparsec.ByteString? > > > > So far, I've been using Data.Attoparsec.ByteString.Char8 and it works > > for my sample files, which are in utf8 or, latin1, or the default > > Windows encoding. > > > > What do you suggest? > > > > Regards, > > > > Pedro Borges > > _______________________________________________ > Beginners mailing list > Beginners at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners > -- -- Kim-Ee -------------- next part -------------- An HTML attachment was scrubbed... URL: From pietro.gra at hotmail.it Tue Feb 21 13:17:14 2023 From: pietro.gra at hotmail.it (Pietro Grandinetti) Date: Tue, 21 Feb 2023 13:17:14 +0000 Subject: [Haskell-beginners] Understanding reason for Monad Message-ID: Hello-- I am going to use the function 'hashPassword' in [1] and I am not able to understand why the result is MonadRandom ByteArray instead of simply being a ByteString (or, ByteArray, or similar). Looking at [2], the only useful thing I can do with a MonadRandom a is to convert it to IO a. Why would a result of this determistic computation be a IO? [1] - https://hackage.haskell.org/package/cryptonite-0.30/docs/Crypto-KDF-BCrypt.html [2] - https://hackage.haskell.org/package/cryptonite-0.30/docs/Crypto-Random-Types.html#t:MonadRandom -------------- next part -------------- An HTML attachment was scrubbed... URL: From ky3 at atamo.com Tue Feb 21 13:52:14 2023 From: ky3 at atamo.com (Kim-Ee Yeoh) Date: Tue, 21 Feb 2023 20:52:14 +0700 Subject: [Haskell-beginners] Understanding reason for Monad In-Reply-To: References: Message-ID: Hi Pietro, The hash algorithm you linked to is not a pure hash function. Using pure hash functions for passwords make the resulting hashes vulnerable to dictionary attacks. The bcrypt algorithm incorporates random data—hence the use of MonadRandom—called a salt, to generate a password hash resistant to dictionary attacks. (You’d probably get better answers to such questions on the cafe mailing list. You seem to be well beyond the LYAH level, more power to you!) Best, Kim-Ee On Tue, Feb 21, 2023 at 8:17 PM Pietro Grandinetti wrote: > Hello-- > > I am going to use the function 'hashPassword' in [1] and I am not able to > understand why the result is MonadRandom ByteArray instead of simply > being a ByteString (or, ByteArray, or similar). Looking at [2], the only > useful thing I can do with a MonadRandom a is to convert it to IO a. Why > would a result of this determistic computation be a IO? > > [1] - > https://hackage.haskell.org/package/cryptonite-0.30/docs/Crypto-KDF-BCrypt.html > [2] - > https://hackage.haskell.org/package/cryptonite-0.30/docs/Crypto-Random-Types.html#t:MonadRandom > _______________________________________________ > Beginners mailing list > Beginners at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners > -- -- Kim-Ee -------------- next part -------------- An HTML attachment was scrubbed... URL: From travis.cardwell at extrema.is Wed Feb 22 07:15:27 2023 From: travis.cardwell at extrema.is (Travis Cardwell) Date: Wed, 22 Feb 2023 16:15:27 +0900 Subject: [Haskell-beginners] Understanding reason for Monad In-Reply-To: References: Message-ID: Hi Pietro, Haskell type classes provide ad-hoc polymorphism. Essentially, they allow you to write a function that works with more than one concrete type. Function `hashPassword` has the following type: hashPassword :: (MonadRandom m, ByteArray password, ByteArray hash) => Int -> password -> m hash It has three constraints (listed before the double arrow): Constraint `MonadRandom m` means that the function works in any monad that has a `MonadRandom` instance and can therefore be used to generate random values. If you click `MonadRandom` in the documentation, you can see which instances are built in. There is an `IO` instance, so you can run `hashPassword` in the `IO` monad. Constraint `ByteArray password` means that the function accepts a password of any type that has a `ByteArray` instance. If you click `ByteArray` in the documentation, you can see which instances are built in. There is a `ByteString` instance, so `hashPassword` can handle `ByteString` password arguments. Constraint `ByteArray hash` means that the function can return a hash of any type that has a `ByteArray` instance. The concrete type may be determined by how the return type is used; if your code expects a `ByteString` hash, then that is what you get. Here is a simple demo that hashes the first argument, or `password` if no arguments are provided: {-# LANGUAGE OverloadedStrings #-} module Main (main) where -- https://hackage.haskell.org/package/base import System.Environment (getArgs) -- https://hackage.haskell.org/package/base16-bytestring import qualified Data.ByteString.Base16 as Base16 -- https://hackage.haskell.org/package/cryptonite import qualified Crypto.KDF.BCrypt as BCrypt -- https://hackage.haskell.org/package/text import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.Text.Encoding.Error as TEE import qualified Data.Text.IO as TIO main :: IO () main = do password <- T.pack . head . (++ ["password"]) <$> getArgs TIO.putStrLn $ "password: " <> password hashBS <- BCrypt.hashPassword 11 $ TE.encodeUtf8 password let hashHex = TE.decodeUtf8With TEE.lenientDecode $ Base16.encode hashBS TIO.putStrLn $ "hash: " <> hashHex This demo runs in the `IO` monad, the password is passed to `hashPassword` as a `ByteString`, and the hash returned by `hashPassword` is a `ByteString`. Note that the password is encoded as a `ByteString` via `Text` so that it supports UTF-8. $ hash-password パスワード password: パスワード hash: 24326224313124486e474157773750493667744b6b6270354451613275516863667a68702e6756727a414b474542716751755371314949613549314b Cheers, Travis From travis.cardwell at extrema.is Wed Feb 22 20:22:22 2023 From: travis.cardwell at extrema.is (Travis Cardwell) Date: Thu, 23 Feb 2023 05:22:22 +0900 Subject: [Haskell-beginners] Understanding reason for Monad In-Reply-To: References: Message-ID: Hi Pietro, The reply I wrote yesterday was sent when I ran out of time, but I have since realized that I failed to get to the point. Sorry about that! The demo shows that you can use `hashPassword` with `IO` and `ByteString`, as if the type was the following: hashPassword :: Int -> ByteString -> IO ByteString The use of type classes allows the function to be used with other types as well, however. This makes the function more flexible for different people's needs. For example, a project may use this function in a user registration request handler. Perhaps that request handler needs to write to a database as well as perform logging. It can be implemented using a monad that has the context required for these features, so that the it can do all of these things without being concerned with configuration details. The monad may have the following constraints: * `MonadRandom m` so that `hashPassword` can be used * `MonadLogger m` so that it can log in the configured way * `MonadDatabase m` (using some application-defined monad) so that it has access to the application database If `hashPassword` had the above `IO` type, then it would not work in such an application monad directly. If there is a `MonadIO` instance, then one could use `liftIO`, but some application monads specifically do *not* have `MonadIO` instances to make it more difficult to simply run things in `IO`. I am out of time again, but I hope this two-part reply is helpful. Cheers, Travis From pietro.gra at hotmail.it Sun Feb 26 09:26:23 2023 From: pietro.gra at hotmail.it (Pietro Grandinetti) Date: Sun, 26 Feb 2023 09:26:23 +0000 Subject: [Haskell-beginners] Reproduce example in Crypto.JWT Message-ID: I am try to reproduce the first example here: https://hackage.haskell.org/package/jose-0.10/docs/Crypto-JWT.html I can't seem to succeed with importing the function `runJOSE` ghci> import Crypto.JWT ghci> :t runJOSE :1:1: error: Variable not in scope: runJOSE ghci> import Crypto.JOSE.Error --https://github.com/frasertweedale/hs-jose/blob/master/src/Crypto/JOSE/Error.hs ghci> :t runJOSE :1:1: error: Variable not in scope: runJOSE Can you point out what I am missing? Thanks. -------------- next part -------------- An HTML attachment was scrubbed... URL: From fa-ml at ariis.it Sun Feb 26 10:55:39 2023 From: fa-ml at ariis.it (Francesco Ariis) Date: Sun, 26 Feb 2023 11:55:39 +0100 Subject: [Haskell-beginners] Reproduce example in Crypto.JWT In-Reply-To: References: Message-ID: Ciao Pietro, Il 26 febbraio 2023 alle 09:26 Pietro Grandinetti ha scritto: > I am try to reproduce the first example here: https://hackage.haskell.org/package/jose-0.10/docs/Crypto-JWT.html > I can't seem to succeed with importing the function `runJOSE` > > ghci> import Crypto.JWT > ghci> :t runJOSE I `cabal get` jose-0.10, `cabal repl` and then λ> import Crypto.JWT λ> :t runJOSE runJOSE :: JOSE e m a -> m (Either e a) I would check if you the latest jose, and what :browse Crypto.JWT says —F From pietro.gra at hotmail.it Sun Feb 26 11:57:08 2023 From: pietro.gra at hotmail.it (Pietro Grandinetti) Date: Sun, 26 Feb 2023 11:57:08 +0000 Subject: [Haskell-beginners] Reproduce example in Crypto.JWT In-Reply-To: References: Message-ID: Francesco, Thanks. I am using stack and the version doesn't match: `stack ls dependencies` shows `jose 0.9`. I can't seem to find a way to upgrade it even if I set `jose >= 0.10` in the package.yaml file: $ stack build WARNING: Ignoring uhask's bounds on jose (>=0.10); using jose-0.9. Reason: allow-newer enabled. uhask> build (lib + exe) Preprocessing library for uhask-0.1.0.0.. Building library for uhask-0.1.0.0.. [4 of 7] Compiling Modules.JWS ..... <--- Errors about runJOSE not defined $ stack install jose-0.10 No latest package revision found for: jose, dependency callstack: [] Google and SO didn't help so far. Can you suggest something to resolve this? Thanks. ________________________________ From: Beginners on behalf of Francesco Ariis Sent: Sunday, February 26, 2023 11:55 AM To: beginners at haskell.org Subject: Re: [Haskell-beginners] Reproduce example in Crypto.JWT Ciao Pietro, Il 26 febbraio 2023 alle 09:26 Pietro Grandinetti ha scritto: > I am try to reproduce the first example here: https://hackage.haskell.org/package/jose-0.10/docs/Crypto-JWT.html > I can't seem to succeed with importing the function `runJOSE` > > ghci> import Crypto.JWT > ghci> :t runJOSE I `cabal get` jose-0.10, `cabal repl` and then λ> import Crypto.JWT λ> :t runJOSE runJOSE :: JOSE e m a -> m (Either e a) I would check if you the latest jose, and what :browse Crypto.JWT says —F _______________________________________________ Beginners mailing list Beginners at haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners -------------- next part -------------- An HTML attachment was scrubbed... URL: From fa-ml at ariis.it Sun Feb 26 12:05:41 2023 From: fa-ml at ariis.it (Francesco Ariis) Date: Sun, 26 Feb 2023 13:05:41 +0100 Subject: [Haskell-beginners] Reproduce example in Crypto.JWT In-Reply-To: References: Message-ID: Il 26 febbraio 2023 alle 11:57 Pietro Grandinetti ha scritto: > Francesco, > > Thanks. > I am using stack and the version doesn't match: `stack ls dependencies` shows `jose 0.9`. I can't seem to find a way to upgrade it even if I set `jose >= 0.10` in the package.yaml file: This is `jose` in stackage: https://www.stackage.org/lts-20.12/package/jose-0.9 So switching to Nightly will do —F From pietro.gra at hotmail.it Sun Feb 26 14:11:15 2023 From: pietro.gra at hotmail.it (Pietro Grandinetti) Date: Sun, 26 Feb 2023 14:11:15 +0000 Subject: [Haskell-beginners] Reproduce example in Crypto.JWT In-Reply-To: References: Message-ID: Francesco, That solved it. Would it be better to use nightly only for this package and, if so, is this possible within the package.yaml file? Thanks. ________________________________ From: Beginners on behalf of Francesco Ariis Sent: Sunday, February 26, 2023 1:05 PM To: beginners at haskell.org Subject: Re: [Haskell-beginners] Reproduce example in Crypto.JWT Il 26 febbraio 2023 alle 11:57 Pietro Grandinetti ha scritto: > Francesco, > > Thanks. > I am using stack and the version doesn't match: `stack ls dependencies` shows `jose 0.9`. I can't seem to find a way to upgrade it even if I set `jose >= 0.10` in the package.yaml file: This is `jose` in stackage: https://www.stackage.org/lts-20.12/package/jose-0.9 So switching to Nightly will do —F _______________________________________________ Beginners mailing list Beginners at haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners -------------- next part -------------- An HTML attachment was scrubbed... URL: From travis.cardwell at extrema.is Mon Feb 27 11:53:28 2023 From: travis.cardwell at extrema.is (Travis Cardwell) Date: Mon, 27 Feb 2023 20:53:28 +0900 Subject: [Haskell-beginners] Understanding reason for Monad In-Reply-To: References: Message-ID: Hi Pietro, The example in my previous message is that of a web application where the user registration request handler needs to write to a database as well as perform logging. In a production server, connecting to a database usually requires some configuration, and a pool of database connections is kept open and reused for improved performance. Similarly, logging may require configuration that the whole application needs to use in order to log consistently, and a resource may be kept open depending on how logging is configured. The server can initialize this state and provide it to various components such as request handlers. This state is what I referred to as a "context." It can be passed explicitly as arguments, but there are various other ways to manage it. For example, one popular design is to use a "Reader" monad, which essentially passes the state as an implicit argument to all functions that use the monad. Type classes such as `MonadDatabase` or `MonadLogger` may provide database and logging APIs utilizing the state made available by the Reader. Providing a meaningful minimal example is difficult because this type of abstraction is most beneficial in large applications, but here is a Reader example that is minimal for the points that I want to make: module Main (main) where -- https://hackage.haskell.org/package/base import Control.Monad.IO.Class (MonadIO(liftIO)) import Data.Bool (bool) import System.Environment (getArgs) -- https://hackage.haskell.org/package/transformers import Control.Monad.Trans.Reader (ReaderT(runReaderT), asks) data Locale = En | It class HasLocale a where getLocale :: a -> Locale instance HasLocale Locale where getLocale = id class MonadLocale m where askLocale :: m Locale instance (HasLocale r, Monad m) => MonadLocale (ReaderT r m) where askLocale = asks getLocale putLanguage :: (MonadIO m, MonadLocale m) => m () putLanguage = do locale <- askLocale liftIO . putStrLn $ case locale of En -> "English" It -> "Italiano" putHelloWorld :: (MonadIO m, MonadLocale m) => m () putHelloWorld = do locale <- askLocale liftIO . putStrLn $ case locale of En -> "Hello world!" It -> "Ciao mondo!" app :: (MonadIO m, MonadLocale m) => m () app = do putLanguage putHelloWorld main :: IO () main = do locale <- bool En It . elem "--it" <$> getArgs runReaderT app locale In this example, the state/context is simply a `Locale` value, which defaults to `En`. The `main` function checks if string `--it` is passed as an argument and configures the locale to `It` in that case. The final line runs the `app` function using a `ReaderT` monad transformer with the locale as the "environment." The `app` function, as well as all functions that it calls in the same monad, have access to this environment. Type class `HasLocale` just provides a `getLocale` function for getting a `Locale` value from some possibly larger value. The instance is the trivial case of `Locale` itself. Type class `MonadLocale` provides a locale API, just `askLocale` in this case. In a monad that implements `MonadLocale`, the `askLocale` function is able to get the locale. The instance provides a way to do this in a Reader monad that has an environment with a `HasLocale` instance. In this minimal example, the Reader environment is a `Locale` value, so that trivial `HasLocale` instance is used. The remaining three functions implement the example application. They do not specify a concrete monad; they instead specify constraints on the monad, allowing them to run in any monad that meets those constraints. The `MonadIO m` constraint is required to use `liftIO . putStrLn` in order to print to the screen, and the `MonadLocale m` constraint is required to get the configured locale. In this example, they are run in concrete monad `ReaderT Locale IO`, but note that they could also be run in different monads as long as the constraints are satisfied. The `app` function calls `putLanguage` and then `putHelloWorld`, and both of these functions are able to use `askLocale` to get the configured locale. $ minimal-context English Hello world! $ minimal-context --it Italiano Ciao mondo! The architecture/design of a project/program depends on the needs. In some programs, explicitly passing context as arguments is the best approach. In others, even `MonadIO` should be avoided, since `IO` makes anything possible. Getting back to your original question, the use of type classes allows a library author to implement functions that work across a wide variety of coding styles. Cheers, Travis On Sun, Feb 26, 2023 at 6:35 PM Pietro Grandinetti wrote: > Hi Travis, > > Thanks. This was indeed helpful. I think I haven't grasped the concept of "context" yet. Do you know any minimal example that shows this? > > Thanks. From pietro.gra at hotmail.it Tue Feb 28 14:01:15 2023 From: pietro.gra at hotmail.it (Pietro Grandinetti) Date: Tue, 28 Feb 2023 14:01:15 +0000 Subject: [Haskell-beginners] Understanding reason for Monad In-Reply-To: References: Message-ID: Travis, Thank you very much. I have a question about the `putLanguage` function below. ________________________________ module Main (main) where -- https://hackage.haskell.org/package/base import Control.Monad.IO.Class (MonadIO(liftIO)) import Data.Bool (bool) import System.Environment (getArgs) -- https://hackage.haskell.org/package/transformers import Control.Monad.Trans.Reader (ReaderT(runReaderT), asks) data Locale = En | It class HasLocale a where getLocale :: a -> Locale instance HasLocale Locale where getLocale = id class MonadLocale m where askLocale :: m Locale instance (HasLocale r, Monad m) => MonadLocale (ReaderT r m) where askLocale = asks getLocale putLanguage :: (MonadIO m, MonadLocale m) => m () putLanguage = do locale <- askLocale liftIO . putStrLn $ case locale of En -> "English" It -> "Italiano" I understand that the result type, in this case `MonadLocale m => m ()`, determines in what context the function `askLocale` is resolved. But what would happen if the function type was putLanguage' :: (MonadOut m, MonadIn v) => v () -> m () -- both MonadOut and MonadIn are instances of MonadLocale putLanguage' = do locale <- askLocale ... -- other things which `askLocale` function would be used? putHelloWorld :: (MonadIO m, MonadLocale m) => m () putHelloWorld = do locale <- askLocale liftIO . putStrLn $ case locale of En -> "Hello world!" It -> "Ciao mondo!" app :: (MonadIO m, MonadLocale m) => m () app = do putLanguage putHelloWorld main :: IO () main = do locale <- bool En It . elem "--it" <$> getArgs runReaderT app locale In this example, the state/context is simply a `Locale` value, which defaults to `En`. The `main` function checks if string `--it` is passed as an argument and configures the locale to `It` in that case. The final line runs the `app` function using a `ReaderT` monad transformer with the locale as the "environment." The `app` function, as well as all functions that it calls in the same monad, have access to this environment. Type class `HasLocale` just provides a `getLocale` function for getting a `Locale` value from some possibly larger value. The instance is the trivial case of `Locale` itself. Type class `MonadLocale` provides a locale API, just `askLocale` in this case. In a monad that implements `MonadLocale`, the `askLocale` function is able to get the locale. The instance provides a way to do this in a Reader monad that has an environment with a `HasLocale` instance. In this minimal example, the Reader environment is a `Locale` value, so that trivial `HasLocale` instance is used. The remaining three functions implement the example application. They do not specify a concrete monad; they instead specify constraints on the monad, allowing them to run in any monad that meets those constraints. The `MonadIO m` constraint is required to use `liftIO . putStrLn` in order to print to the screen, and the `MonadLocale m` constraint is required to get the configured locale. In this example, they are run in concrete monad `ReaderT Locale IO`, but note that they could also be run in different monads as long as the constraints are satisfied. The `app` function calls `putLanguage` and then `putHelloWorld`, and both of these functions are able to use `askLocale` to get the configured locale. $ minimal-context English Hello world! $ minimal-context --it Italiano Ciao mondo! The architecture/design of a project/program depends on the needs. In some programs, explicitly passing context as arguments is the best approach. In others, even `MonadIO` should be avoided, since `IO` makes anything possible. Getting back to your original question, the use of type classes allows a library author to implement functions that work across a wide variety of coding styles. Cheers, Travis On Sun, Feb 26, 2023 at 6:35 PM Pietro Grandinetti wrote: > Hi Travis, > > Thanks. This was indeed helpful. I think I haven't grasped the concept of "context" yet. Do you know any minimal example that shows this? > > Thanks. -------------- next part -------------- An HTML attachment was scrubbed... URL: