{"id":20993325,"url":"https://github.com/diego-vicente/monadic-gcd","last_synced_at":"2026-03-05T05:32:12.456Z","repository":{"id":86026695,"uuid":"90557627","full_name":"diego-vicente/monadic-gcd","owner":"diego-vicente","description":"Learning exercise (GCD) with explanation about monads","archived":false,"fork":false,"pushed_at":"2017-05-08T15:46:24.000Z","size":9,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-12T05:10:08.377Z","etag":null,"topics":["functional-programming","functional-programming-examples","monads"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/diego-vicente.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-07T19:49:31.000Z","updated_at":"2024-10-11T09:07:49.000Z","dependencies_parsed_at":"2023-04-17T22:55:10.167Z","dependency_job_id":null,"html_url":"https://github.com/diego-vicente/monadic-gcd","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/diego-vicente/monadic-gcd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diego-vicente%2Fmonadic-gcd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diego-vicente%2Fmonadic-gcd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diego-vicente%2Fmonadic-gcd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diego-vicente%2Fmonadic-gcd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/diego-vicente","download_url":"https://codeload.github.com/diego-vicente/monadic-gcd/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diego-vicente%2Fmonadic-gcd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30111743,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T03:40:26.266Z","status":"ssl_error","status_checked_at":"2026-03-05T03:39:15.902Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","functional-programming-examples","monads"],"created_at":"2024-11-19T07:14:33.086Z","updated_at":"2026-03-05T05:32:12.418Z","avatar_url":"https://github.com/diego-vicente.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"#+Title:  Learning Monads in an Example\n#+Author: Diego Vicente Martín\n#+Email:  diegovicente@protonmail.com\n\n\nI have been struggling with the concept of monads for a long time, and since I\nam writing my bachelor thesis in Haskell, it was about time to face this\nproblem head-on, not by only using one of the provided monads but to trying to\nimplement on of my own. Since I am not an expert in the field, I have to\nrecommend you some previous literature that might help you more than I will:\n\n- [[http://learnyouahaskell.com/][Learn You a Haskell for Great Good]] is a super fun book for beginners in\n  Haskell that you can read online. It has chapters on functors and monads.\n- [[http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf][Philip Wadler's papers on monads for functional programming]], that explain the\n  basic concepts of monads and goes through the main types of monads.\n\nIf you have already read this but you are not sure yet on how to implement a\nmonad in Haskell, maybe this little example will help you and you will save\nvisiting some of the dead ends I had to face.\n\nOnce again I am not an expert, so feel free to open an issue to give any kind\nof feedback: improvements of the explanation, corrections on concepts, better\ntips, more literature... Thank you, dear reader!\n\n* An example: Euclidean Algorithm\n\n** A pure approach\n\nOne of the easiest and prettiest examples to program in Haskell is the\n[[https://en.wikipedia.org/wiki/Euclidean_algorithm][Euclidean Algorithm]] to find the Greatest Common Divisor of two given\nnumbers. We can see the code for that function here:\n\n#+BEGIN_SRC haskell\nsimpleGCD :: (Integral a) =\u003e a -\u003e a -\u003e a\nsimpleGCD a b\n  | a \u003c b          = simpleGCD b a\n  | a `mod` b == 0 = b\n  | otherwise      = simpleGCD b (a `mod` b)\n#+END_SRC\n\nThis function receives to integer numbers, and computes their remainder\noperation recursively until 0 is found. This codes exhibits and it works like a\ncharm: \n\n#+BEGIN_SRC\nλ\u003e simpleGCD 9282 12376\n3094\n(0.01 secs, 4128256 bytes)\nλ\u003e simpleGCD 9293 12376\n1\n(0.01 secs, 4129080 bytes)\n#+END_SRC\n\nHowever, what if we want to get a trace of the execution performed? In a\ndifferent language like Python we could think of printing the steps in each\niteration, but that implies secondary effects and is not that easy to do\nsomething like that in a purely functional environment like the one Haskell\noffers. Another option is to create a type ~Log~, that contains both the value\nand the a list of strings:\n\n#+BEGIN_SRC haskell\ndata Log a = Log { getValue :: a, getLogs :: [String] } deriving Show\n#+END_SRC\n\nIn that list of strings, we can add each of the steps performed in the\nrecursive call, but that will create a huge mess and the code will be barely\nreadable. What we can do is to check if it makes sense to implement ~Log~ as a\nmonad.\n\n** Turning ~Log~ into a monad\n\nIn Haskell, for a type to be a monad it has to be also a functor and\napplicative. Don't worry though, all monads are by definition functor and\napplicatives, so if you really need to turn your data into a monad there will\nbe no problem. It is a healthy exercise to check if your data makes sense as a\nmonad.\n\nTo make a ~Log~ an instance of the ~Functor~ class, we need to define its\nbehavior with ~fmap~:\n\n#+BEGIN_SRC haskell\ninstance Functor Log where\n  fmap f (Log x logs) = Log (f x) logs\n#+END_SRC\n\nHere, we define that every time that a function ~f :: a -\u003e b~ is mapped to a\n~Log a~, we want to apply it to the value and let the list of logs as it\nis. Next step is to define the data as ~Applicative~, which implies the next\nfunctions:\n\n#+BEGIN_SRC haskell\ninstance Applicative Log where\n  pure x = Log x []\n  Log f log \u003c*\u003e Log x log' = Log (f x) (log ++ log')\n#+END_SRC\n\nWhere we define that ~pure~ can turn a value into a ~Log~ by creating an object\nwith that value and an empty list of logs. The ~\u003c*\u003e~ operator seems a little\nbit more obscure, but in case there is a function contained in a ~Log~ it\nshould be appended to the value of another ~Log~ and both lists of logs should\nbe appended. This makes perfect sense when we think about the creation of\npartial functions in Haskell.\n\nFinally, we are able now to make ~Log~ an instance of ~Monad~. To do so, we\nmust define:\n\n#+BEGIN_SRC haskell\ninstance Monad Log where\n  return = pure\n  (Log x log) \u003e\u003e= f = let Log y new = f x in Log y (log ++ new)\n#+END_SRC\n\n~return~ is used to include a new value in a ~Log~, so we can reuse the\ndefinition of ~pure~. On the other hand, the bind operator (~\u003e\u003e=~) defines the\nbehavior we have been trying to achieve: every time we want to bind a ~Log~\nobject to the results of a function that takes the value of a ~Log~ and returns\na new log, we take the value returned by the function but we append the logs of\nthe new function to the list of the old ones.\n\nAlthough it may seem strange to do it like so (using ~\u003e\u003e=~), we need to notice\nthat this is the real operators that a do-block uses as syntactic sugar; taking\nthis into account we could create a function to store the new logs in a dummy\n~Log~ before being appended to the object by the end of recursion:\n\n#+BEGIN_SRC haskell\nsaveLog :: String -\u003e Log ()\nsaveLog str = Log () [str]\n#+END_SRC\n\nAlthough this function may seem unnecessary (and it kind of is), it provides a\ncleaner syntax and it is actually replicated in the provided ~Writer~ monad as\n~tell~.\n\nUsing all these pieces, we can finally update the algorithm to perform the\nlogging of operations! In each guard (except the swapping one) we just need to\ncreate a do-block were we log a message and then bind a value to that\nblock. The final version of the function is:\n\n#+BEGIN_SRC haskell\nlogGCD :: (Integral a, Show a) =\u003e a -\u003e a -\u003e Log a\nlogGCD x y\n | x \u003c y = logGCD y x\n | y == 0 = do\n     saveLog $ \"Greatest Common Divisor found: \" ++ show x\n     return x\n | otherwise = do\n     saveLog $ show x ++ \" mod(\" ++ show y ++ \") = \" ++ show (x `mod` y)\n     logGCD y (x `mod` y)\n#+END_SRC\n\nWe can try the function with the ~stepsGCD~ function that will print all the\nlogs in a nice way:\n\n#+BEGIN_SRC \nλ\u003e stepsGCD 9282 12376\n12376 mod(9282) = 3094\n9282 mod(3094) = 0\nGreatest Common Divisor found: 3094\n(0.01 secs, 2575200 bytes)\nλ\u003e stepsGCD 9293 12376\n12376 mod(9293) = 3083\n9293 mod(3083) = 44\n3083 mod(44) = 3\n44 mod(3) = 2\n3 mod(2) = 1\n2 mod(1) = 0\nGreatest Common Divisor found: 1\n(0.01 secs, 2575400 bytes)\n#+END_SRC\n\nWe can see how now the trace is nicely printed, and we have only modified the\nGCD function as much as we would have done it in an imperative language. Monads\nprinciple advantage is modularity, that helps us isolate the secondary effects\nkeeping the purity. \n\n** The Monad laws\n\nIt is really important to understand that the monad type comes with some laws\nthat your type has to fulfill. These laws are:\n\n- The *left-identity*: Basically, that ~return x \u003e\u003e= f~ should have the exact\n  same result as ~f x~. By trying our type with ~logGCD~:\n\n#+BEGIN_SRC\nλ\u003e return 3 \u003e\u003e= (logGCD 5)\nLog {getValue = 1, getLogs = [\"5 mod(3) = 2\",\"3 mod(2) = 1\",\"2 mod(1) = 0\",\n\"Greatest Common Divisor found: 1\"]}\nλ\u003e (logGCD 5) 3\nLog {getValue = 1, getLogs = [\"5 mod(3) = 2\",\"3 mod(2) = 1\",\"2 mod(1) = 0\",\n\"Greatest Common Divisor found: 1\"]}\n#+END_SRC\n\n- The *right-identity*: Similar to the previous one, ~m \u003e\u003e= return~ should be\n  equal to ~m~.\n\n#+BEGIN_SRC\nλ\u003e let m = logGCD 5 3\nλ\u003e m\nLog {getValue = 1, getLogs = [\"5 mod(3) = 2\",\"3 mod(2) = 1\",\"2 mod(1) = 0\",\n\"Greatest Common Divisor found: 1\"]}\nλ\u003e m \u003e\u003e= return\nLog {getValue = 1, getLogs = [\"5 mod(3) = 2\",\"3 mod(2) = 1\",\"2 mod(1) = 0\",\n\"Greatest Common Divisor found: 1\"]}\n#+END_SRC\n\n- The *associativity law*, that states that a chain of monadic operations\n  should not be dependant on its nesting.\n\n#+BEGIN_SRC\nλ\u003e return 14 \u003e\u003e= (logGCD 35) \u003e\u003e= (logGCD 21)\nLog {getValue = 7, getLogs = [\"35 mod(14) = 7\",\"14 mod(7) = 0\",\n\"Greatest Common Divisor found: 7\",\"21 mod(7) = 0\",\n\"Greatest Common Divisor found: 7\"]}\nλ\u003e ( return 14 \u003e\u003e= (logGCD 35) ) \u003e\u003e= (logGCD 21)\nLog {getValue = 7, getLogs = [\"35 mod(14) = 7\",\"14 mod(7) = 0\",\n\"Greatest Common Divisor found: 7\",\"21 mod(7) = 0\",\n\"Greatest Common Divisor found: 7\"]}\nλ\u003e return 14 \u003e\u003e= (\\x -\u003e ((logGCD 35) x) \u003e\u003e= (logGCD 14))\nLog {getValue = 7, getLogs = [\"35 mod(14) = 7\",\"14 mod(7) = 0\",\n\"Greatest Common Divisor found: 7\",\"14 mod(7) = 0\",\n\"Greatest Common Divisor found: 7\"]}\n#+END_SRC\n\nBecause yes, thanks to the Monad value, we can chain GCD operations to obtain\ncombined logs. Super cool, isn't it?\n\n\n\n** Do I really have to do all this?\n\nActually, *no*. This was just an example on how to build a super simple monadic\ntype, but it is so simple and common that Haskell provides you with its own\nimplementation, ~Control.Monad.Writer~. If you ever feel like you need it in\nthe real world, don't reinvent the wheel and just use the provided\none. However, take also into account that if you need high-performant\ncomputations, the overhead generated by ~Writer~ may be too high and it may be\nworth to check other packages (like [[https://hackage.haskell.org/package/fast-logger][fast-logger]]) or implement your own monad.\n\nAlso, even though I said that it is a healthy exercise to think if your data is\nas well a functor and applicative, but that is indeed not necessary either. If\nyou are struggling with it, you can use the functions provided by the ~Monad~\nclass to define ~Functor~ and ~Applicative~ instances like this:\n\n#+BEGIN_SRC haskell\nimport Control.Monad (liftM, ap)\n\ninstance Functor Log where\n  fmap = liftM\n\ninstance Applicative Log where\n  pure  = return\n  (\u003c*\u003e) = ap\n#+END_SRC\n\nAnd then just focus on defining the ~Monad~ instance. There might be a more\nefficient way to define it, but this should work anyway for all cases. Just be\nsure of checking if the data really makes sense as a monad before actually\nimplementing it as such.\n\n* Further reading\n\nAfter understanding this, it is probably useful to read:\n\n- [[http://members.chello.nl/hjgtuyl/tourdemonad.html][\"A tour of the Haskell Monad functions\" by Henk-Jan van Tuyl]], that describes\n  the different functions that the ~Monad~ class offers in Haskell.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiego-vicente%2Fmonadic-gcd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdiego-vicente%2Fmonadic-gcd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiego-vicente%2Fmonadic-gcd/lists"}