<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div style="font-family: Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0); background-color: rgb(255, 255, 255);" class="elementToProof">
Travis,</div>
<div style="font-family: Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0); background-color: rgb(255, 255, 255);" class="elementToProof">
<br>
</div>
<div style="font-family: Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0); background-color: rgb(255, 255, 255);" class="elementToProof">
Thank you very much. I have a question about the `putLanguage` function below.<br>
</div>
<div id="appendonsend"></div>
<div style="font-family:Calibri,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0)">
<br>
</div>
<hr tabindex="-1" style="display:inline-block; width:98%">
<br>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt">
<div class="PlainText elementToProof"><br>
module Main (main) where<br>
<br>
-- <a href="https://hackage.haskell.org/package/base" data-auth="NotApplicable">
https://hackage.haskell.org/package/base</a><br>
import Control.Monad.IO.Class (MonadIO(liftIO))<br>
import Data.Bool (bool)<br>
import System.Environment (getArgs)<br>
<br>
-- <a href="https://hackage.haskell.org/package/transformers" data-auth="NotApplicable">
https://hackage.haskell.org/package/transformers</a><br>
import Control.Monad.Trans.Reader (ReaderT(runReaderT), asks)<br>
<br>
data Locale = En | It<br>
<br>
class HasLocale a where<br>
getLocale :: a -> Locale<br>
<br>
instance HasLocale Locale where<br>
getLocale = id<br>
<br>
class MonadLocale m where<br>
askLocale :: m Locale<br>
<br>
instance (HasLocale r, Monad m) => MonadLocale (ReaderT r m) where<br>
askLocale = asks getLocale<br>
<br>
putLanguage :: (MonadIO m, MonadLocale m) => m ()<br>
putLanguage = do<br>
locale <- askLocale<br>
liftIO . putStrLn $ case locale of<br>
En -> "English"<br>
It -> "Italiano"<br>
</div>
<div class="PlainText elementToProof"><br>
</div>
<div class="PlainText elementToProof"><font size="2"><span style="font-size: 12pt; font-family: Calibri, Helvetica, sans-serif; color: rgb(0, 0, 0);" class="ContentPasted0">I understand that the result</span></font><span style="font-family: Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
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</span><br>
</div>
<div class="PlainText elementToProof"><br>
</div>
<div class="PlainText elementToProof"><span style="font-family: Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">putLanguage' :: (MonadOut m, MonadIn v) => v () -> m () -- both MonadOut and MonadIn are instances of MonadLocale</span><br>
</div>
<div class="PlainText elementToProof"><span style="font-family: Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">putLanguage' = do</span></div>
<div class="PlainText elementToProof"><span style="font-family: Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);"> locale <- askLocale</span></div>
<div class="PlainText elementToProof"><span style="font-family: Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);"> ... -- other things</span></div>
<div class="PlainText elementToProof"><br>
</div>
<div class="PlainText elementToProof"><span style="font-family: Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">which `askLocale` function would be used?</span><br>
</div>
<div class="PlainText elementToProof"><br>
putHelloWorld :: (MonadIO m, MonadLocale m) => m ()<br>
putHelloWorld = do<br>
locale <- askLocale<br>
liftIO . putStrLn $ case locale of<br>
En -> "Hello world!"<br>
It -> "Ciao mondo!"<br>
</div>
<div class="PlainText elementToProof"><br>
app :: (MonadIO m, MonadLocale m) => m ()<br>
app = do<br>
putLanguage<br>
putHelloWorld<br>
<br>
main :: IO ()<br>
main = do<br>
locale <- bool En It . elem "--it" <$> getArgs<br>
runReaderT app locale<br>
<br>
In this example, the state/context is simply a `Locale` value, which<br>
defaults to `En`. The `main` function checks if string `--it` is passed<br>
as an argument and configures the locale to `It` in that case.<br>
<br>
The final line runs the `app` function using a `ReaderT` monad<br>
transformer with the locale as the "environment." The `app` function,<br>
as well as all functions that it calls in the same monad, have access to<br>
this environment.<br>
<br>
Type class `HasLocale` just provides a `getLocale` function for getting<br>
a `Locale` value from some possibly larger value. The instance is the<br>
trivial case of `Locale` itself.<br>
<br>
Type class `MonadLocale` provides a locale API, just `askLocale` in this<br>
case. In a monad that implements `MonadLocale`, the `askLocale`<br>
function is able to get the locale. The instance provides a way to do<br>
this in a Reader monad that has an environment with a `HasLocale`<br>
instance. In this minimal example, the Reader environment is a `Locale`<br>
value, so that trivial `HasLocale` instance is used.<br>
<br>
The remaining three functions implement the example application. They<br>
do not specify a concrete monad; they instead specify constraints on the<br>
monad, allowing them to run in any monad that meets those constraints.<br>
The `MonadIO m` constraint is required to use `liftIO . putStrLn` in<br>
order to print to the screen, and the `MonadLocale m` constraint is<br>
required to get the configured locale. In this example, they are run<br>
in concrete monad `ReaderT Locale IO`, but note that they could also be<br>
run in different monads as long as the constraints are satisfied.<br>
<br>
The `app` function calls `putLanguage` and then `putHelloWorld`, and<br>
both of these functions are able to use `askLocale` to get the<br>
configured locale.<br>
<br>
$ minimal-context<br>
English<br>
Hello world!<br>
$ minimal-context --it<br>
Italiano<br>
Ciao mondo!<br>
<br>
The architecture/design of a project/program depends on the needs. In<br>
some programs, explicitly passing context as arguments is the best<br>
approach. In others, even `MonadIO` should be avoided, since `IO` makes<br>
anything possible. Getting back to your original question, the use of<br>
type classes allows a library author to implement functions that work<br>
across a wide variety of coding styles.<br>
<br>
Cheers,<br>
<br>
Travis<br>
<br>
<br>
On Sun, Feb 26, 2023 at 6:35 PM Pietro Grandinetti<br>
<pietro.gra@hotmail.it> wrote:<br>
> Hi Travis,<br>
><br>
> 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?<br>
><br>
> Thanks.<br>
</div>
</span></font></div>
</body>
</html>