Fork me on GitHub

Files

mon@razerRamon:~/tmp/haskell/granulate-effects$ ll
total 12K
-rwxr-xr-x 1 mon mon 3.2K Nov 24 20:57 GranulateEffects.hs*
mon@razerRamon:~/tmp/haskell/granulate-effects$

Haskell Code Snippet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env stack
{- stack
--resolver lts-8.21
--install-ghc
runghc
--package http-client
--package http-client-tls
--
-Wall -Werror
-}


module GranulateEffects (main) where

import qualified Data.ByteString.Lazy.Char8 as L8

{- https://haskell-lang.org/library/http-client -}
import Network.HTTP.Client
{- https://hackage.haskell.org/package/http-client-tls -}
import Network.HTTP.Client.TLS (tlsManagerSettings)

{- As Kris Jenkins (@krisajenkins) says: "IO is the SUDO of side-effects" -}
getHtml :: String -> IO String
getHtml url = do
{- To get secure HTML we will need: TLS manager, request and response -}
manager <- newManager tlsManagerSettings
request <- parseRequest url
response <- httpLbs request manager
let html = L8.unpack $ responseBody response

{- Because we aren't limiting effects, we can also log to a file -}
writeFile "./logging.txt" html

return html

{- Lets granulate our effects to what we really want -}
class Monad m =>
MmanagerTLS m where
newManager' :: ManagerSettings -> m Manager

instance MmanagerTLS IO where
newManager' = newManager

class Monad m =>
Mrequest m where
parseRequest' :: String -> m Request

instance Mrequest IO where
parseRequest' = parseRequest

class Monad m =>
Mresponse m where
httpLbs' :: Request -> Manager -> m (Response L8.ByteString)

instance Mresponse IO where
httpLbs' = httpLbs

{- We update the signature and add our granulated prime functions -}
getHtml' :: (MmanagerTLS m, Mrequest m, Mresponse m) => String -> m String
getHtml' url = do
{- To get secure HTML we will need: TLS manager, request and response -}
manager <- newManager' tlsManagerSettings
request <- parseRequest' url
response <- httpLbs' request manager
let html = L8.unpack $ responseBody response

{- And we are no longer allowed to log to a file, only retrieve HTML -}
-- writeFile "./logging.txt" html
{- error:
• Couldn't match type ‘m’ with ‘IO’
‘m’ is a rigid type variable bound by
the type signature for:
getHtml' :: forall (m :: * -> *).
(MmanagerTLS m, Mrequest m, Mresponse m) =>
String -> m String
at GranulateEffects.hs:70:13
Expected type: m ()
Actual type: IO ()
• In a stmt of a 'do' block: writeFile "./logging.txt" html
In the expression:
do { manager <- newManager' tlsManagerSettings;
request <- parseRequest' url;
response <- httpLbs' request manager;
let html = L8.unpack $ responseBody response;
.... }
In an equation for ‘getHtml'’:
getHtml' url
= do { manager <- newManager' tlsManagerSettings;
request <- parseRequest' url;
response <- httpLbs' request manager;
.... }
• Relevant bindings include
getHtml' :: String -> m String (bound at GranulateEffects.hs:71:1)
-}


return html

{- And ofc it's easier to just put everything inside the same monad -}
class Monad m =>
HtmlHttpsM m where
newManager'' :: ManagerSettings -> m Manager
parseRequest'' :: String -> m Request
httpLbs'' :: Request -> Manager -> m (Response L8.ByteString)

instance HtmlHttpsM IO where
newManager'' = newManager
parseRequest'' = parseRequest
httpLbs'' = httpLbs

{- We update the signature and add our granulated prime prime functions -}
getHtml'' :: (HtmlHttpsM m) => String -> m String
getHtml'' url = do
{- To get secure HTML we will need: TLS manager, request and response -}
manager <- newManager'' tlsManagerSettings
request <- parseRequest'' url
response <- httpLbs'' request manager
let html = L8.unpack $ responseBody response

{- And we are still not allowed to log to a file, only retrieve HTML -}
-- writeFile "./logging.txt" html
{- error:
• Couldn't match type ‘m’ with ‘IO’
‘m’ is a rigid type variable bound by
the type signature for:
getHtml'' :: forall (m :: * -> *).
HtmlHttpsM m =>
String -> m String
at GranulateEffects.hs:111:14
Expected type: m ()
Actual type: IO ()
• In a stmt of a 'do' block: writeFile "./logging.txt" html
In the expression:
do { manager <- newManager'' tlsManagerSettings;
request <- parseRequest'' url;
response <- httpLbs'' request manager;
let html = L8.unpack $ responseBody response;
.... }
In an equation for ‘getHtml''’:
getHtml'' url
= do { manager <- newManager'' tlsManagerSettings;
request <- parseRequest'' url;
response <- httpLbs'' request manager;
.... }
• Relevant bindings include
getHtml'' :: String -> m String
(bound at GranulateEffects.hs:112:1)
-}


return html

main :: IO ()
main = do
html0 <- getHtml "https://www.google.dk/"
html1 <- getHtml' "https://www.google.dk/"
html2 <- getHtml'' "https://www.google.dk/"
print $ html0
print $ html1
print $ html2

Haskell Code output:

mon@razerRamon:~/tmp/haskell/granulate-effects$ ./GranulateEffects.hs
<!doctype html> ... </script></div></body></html>
<!doctype html> ... </script></div></body></html>
<!doctype html> ... </script></div></body></html>
mon@razerRamon:~/tmp/haskell/granulate-effects$

Files

mon@razerRamon:~/tmp/haskell/granulate-effects$ ll
total 68K
-rwxr-xr-x 1 mon mon 3.2K Nov 24 20:57 GranulateEffects.hs*
-rw-r--r-- 1 mon mon 46K Nov 24 21:29 logging.txt
mon@razerRamon:~/tmp/haskell/granulate-effects$

References:

comments powered by Disqus