Author Topic: An informal talk about monads  (Read 1335 times)

0 Members and 1 Guest are viewing this topic.

Offline TheWormKill

  • EZ's Scripting Whore
  • Global Moderator
  • Knight
  • *
  • Posts: 257
  • Cookies: 66
  • The Grim Reaper of Worms
    • View Profile
An informal talk about monads
« on: October 01, 2015, 09:02:16 pm »
Well somehow, discussion got to package managers, I mentioned Haskell's cabal, and we ended talking about monads in their own channel. So I'll post the logs because I think it might be a) worth the read b) funny c) an inspiration and maybe a reason to do a second talk of that kind.
Quote from: #monads
[19:32:06] *** Joins: thewormkill (~thewormki@worms.lair)
[19:32:06] *** Server sets mode: +
[19:32:08] *** Server sets mode: +
[19:32:14] *** Joins: lenoch (~lenoch@61476DB6.A460EC41.F860C5F1.IP)
[19:32:28] *** Joins: acaan (~doctor@evilzone-F267F0C0.whyrlpool.com)
[19:34:21] <thewormkill> we're waiting for poly
[19:34:43] <acaan> kk i will go to bring some food i have to eat
[19:34:44] <lenoch> fucking slow guy
[19:34:48] <lenoch> :D
[19:34:49] <thewormkill> :D
[19:35:43] <lenoch> and thewormkill i'm actually serious about giving haskell maybe a go
[19:35:50] <thewormkill> that's great
[19:35:53] <thewormkill> :)
[19:36:16] <lenoch> should learn a functional language someday so why not in the near future
[19:36:49] <thewormkill> second, I'll be back in 5 minutes, dishes await
[19:36:52] *** Joins: Polyphony (~Polyphony@local.host)
[19:36:57] <Polyphony> i'm a shitty student
[19:36:58] <lenoch> oh man
[19:36:59] <lenoch> lol
[19:37:05] <thewormkill> brb
[19:38:47] <Polyphony> i need to start learning some haskell too
[19:38:57] <Polyphony> i've played with it before, just never actually used it
[19:42:20] <thewormkill> so I am back
[19:42:32] <acaan> wooo
[19:42:34] <thewormkill> acaan, lenoch, Polyphony, let's begin
[19:42:38] <Polyphony> kk
[19:43:11] <thewormkill> well, we talk about monads today
[19:43:33] <thewormkill> these are a construct from a field of mathematics called category theory
[19:43:53] <thewormkill> which roughly deals with categorizing things (woo!), mathematical things
[19:44:12] <thewormkill> so, if anyone of you has a question, just interrupt, we have time for that
[19:44:27] <acaan> question
[19:44:34] <thewormkill> so, let's start with simple values...
[19:44:38] <thewormkill> yeah, acaan?
[19:44:51] <acaan> does it categorizes just numbers or some other objects ?
[19:44:58] <thewormkill> objects in general
[19:45:03] <acaan> kk
[19:45:06] <thewormkill> that's the point
[19:45:16] <thewormkill> well, a value is just that, some data
[19:45:34] <thewormkill> using haskell syntax to bind a name to a value looks like so:
[19:45:37] <thewormkill> let a = 5
[19:46:00] <thewormkill> this is not universally valid syntax, but it makes claer what's going on
[19:46:33] <thewormkill> now, the point is, functional languages generally don't allow for *changing* the value of a variable
[19:46:50] <thewormkill> so if you already have a defined, changing what it means isn't possible
[19:47:10] <thewormkill> instead, you can use functions to transform values
[19:47:26] <lenoch> aight seems logical I was thinking of that
[19:47:30] <thewormkill> now, these functions aren't like functions in, say, Ceither
[19:47:37] <thewormkill> *C either
[19:47:44] <thewormkill> in C I can write
[19:47:59] <thewormkill> void a(){some_global++;}
[19:48:11] <thewormkill> which does something but does not return a value
[19:48:35] <thewormkill> so, if we compare it to the mathematical notion of a function, we see some glaring differences
[19:49:08] <thewormkill> in mathematics, a function just maps values of an input domain to values of an output domain, so that you have unambiguous results
[19:49:27] <thewormkill> now we see that this notion does not apply to void a, because it behaves very differently
[19:49:44] <thewormkill> instead of just computing a value, it... well what does it? any ideas?
[19:49:54] <acaan> changes it
[19:49:59] <thewormkill> changes what?
[19:50:04] <acaan> value
[19:50:13] <acaan> of a global, side efect
[19:50:13] <Polyphony> a is just a pointer
[19:50:39] <thewormkill> actually both of you are right to some extend
[19:50:57] <Polyphony> it's not a function because there's no output
[19:50:58] <thewormkill> 1. it does not compute a value, so it has nothing to return (useless as a mathematical function)
[19:51:10] <Polyphony> ^^ "mathematical function"
[19:51:20] <lenoch> it's just a subroutien
[19:51:31] <Polyphony> yeah, it's just a chunk of instruction
[19:51:37] <thewormkill> 2. it does something else, which is modifying global state
[19:51:55] <thewormkill> so, to sum up, it is not a real function, but a procedure.
[19:52:01] <Polyphony> ah
[19:52:11] <thewormkill> this means that it does not behave well in some contexts
[19:52:20] <thewormkill> think of concurrency
[19:52:57] <thewormkill> if you have multiple threads, the changes they apply to some data might be interfering with each other
[19:53:39] <thewormkill> so some people thought that it might be a good idea to avoid these side-effects as they are called, what acaan already mentioned
[19:53:46] <thewormkill> and I forgot to honour
[19:54:38] <thewormkill> now, let's take a different approach of thinking, or simply get back to the point we are at now from a different direction
[19:55:17] <thewormkill> so if data is just a bunch of values and we can calculate the results of functions on those to get *new* values
[19:55:27] <thewormkill> how do we distinguish values?
[19:55:32] <thewormkill> any ideas?
[19:55:38] <acaan> we dont  ?
[19:56:04] <acaan> no
[19:56:06] <acaan> MONAD
[19:56:08] <acaan> :O
[19:56:12] <thewormkill> lol
[19:56:21] <thewormkill> okay, I'll spoiler it
[19:56:26] <thewormkill> adding context to values
[19:56:37] <thewormkill> sounds easy, but what does that mean?
[19:56:46] <acaan> wait
[19:56:50] <thewormkill> well, let's introduce a new concept...
[19:56:53] <thewormkill> yeah?
[19:56:59] <acaan> what do you mean by adding context
[19:57:24] <thewormkill> well, say you have a function
[19:57:54] <thewormkill> a pure function (which is how funcional programming calls mathematical functions with no side effects)
[19:58:06] <thewormkill> code follows:
[19:58:25] <thewormkill> f(x) = 1 / x
[19:58:32] <thewormkill> (not haskell but w/e)
[19:58:54] <thewormkill> it is certainly nothing special
[19:59:09] <thewormkill> but, if you use it in source code, what will happen if we pass x=0
[19:59:12] <thewormkill> ?
[19:59:26] <lenoch> infinity
[19:59:27] <acaan> undefined
[19:59:32] <Polyphony> "undefined behavior" lol
[19:59:40] <acaan> error
[19:59:41] <Polyphony> actually i don't know, i just love C
[19:59:52] <lenoch> you can't divide by 0
[19:59:52] <acaan> i love U
[19:59:52] <thewormkill> well, actually you crash your proggram
[19:59:54] <acaan> LOL
[20:00:02] <thewormkill> because you can't divide by 0
[20:00:23] <thewormkill> it doesn't make sense in maths, so why should it make sense in our program?
[20:00:26] <lenoch> well I heard it is something with infinity and stuff
[20:00:29] <lenoch> but yeah you can't
[20:00:30] <lenoch> :p
[20:00:47] <lenoch> because um we want to bend the laws of space
[20:00:54] <acaan> and time
[20:00:54] <thewormkill> well, you could just check whether x is 0 but that leaves you with deciding what to do now
[20:01:19] <Polyphony> "floating point exception (core dumped)"
[20:01:26] <Polyphony> that's what happens with c
[20:01:43] <thewormkill> so, basically we conclude that our function can *fail*
[20:01:57] <lenoch> kinda like exceptions
[20:01:59] <Polyphony> set errno and return NULL
[20:02:26] <thewormkill> well, that's a way of representing failure, but it certainly isn't beautiful
[20:02:35] <thewormkill> ...or not feasible in a pure context
[20:02:39] <Polyphony> lol
[20:02:43] <acaan> well just say undefined
[20:02:55] <thewormkill> so instead we alter the output range of our function
[20:02:55] <Polyphony> can we just pause for a moment, i'm gonna switch computers real quick
[20:02:56] <acaan> its nothing
[20:03:00] <thewormkill> Polyphony: sure
[20:03:11] <lenoch> ok i'm reboot to the linoox then
[20:03:13] <Polyphony> kk, it'll take like 5 seconds
[20:03:14] <thewormkill> acaan: sure, but we're coming to that
[20:03:20] <thewormkill> we'll wait
[20:03:27] <acaan> sure
[20:03:47] *** lenoch is now known as lenoch_away
[20:04:14] <Polyphony> o/
[20:04:22] <Polyphony> i'm back, told you it'd be quick
[20:04:44] <thewormkill> lenoch left
[20:05:33] <thewormkill> still left
[20:05:35] <acaan> it connection i gues
[20:06:03] <thewormkill> no he's rebooting
[20:06:23] *** lenoch_away is now known as lenoch
[20:06:30] <thewormkill> oh, we can proceedd
[20:06:45] <thewormkill> well, acaan proposed to add undefined to the range of our function
[20:06:45] <lenoch> fucking windows update
[20:07:16] <thewormkill> that makes sense, but if we pass our result on to other functions (look up composition if you don't know what that is)
[20:07:34] <thewormkill> , we need those functions to know of undefined
[20:07:37] <thewormkill> meh, ugly
[20:07:54] <thewormkill> so, instead we decide to add the *context of failure*
[20:07:56] <lenoch> can't you do an if?
[20:08:15] <thewormkill> lenoch: we do, but that's ugly if we need to repeat it
[20:08:59] <Polyphony> so how does haskell handle it?
[20:09:01] <lenoch> so what do we use?
[20:09:04] <thewormkill> so, let's redefine the range of our function to two values: "Nothing" and "Just x"
[20:09:06] <acaan> MONAD
[20:09:18] <thewormkill> where x is the result of our computation
[20:09:32] <acaan> the same function f(x)=1/x ?
[20:09:44] <thewormkill> we basically pull out undefined from the range of results and handle it dufferently
[20:09:53] <thewormkill> acaan: yes, with a quirk
[20:09:55] <thewormkill> observe
[20:10:06] <thewormkill> f(x) = 1/x if x != 0
[20:10:15] <thewormkill> f(x) = Nothing otherwise
[20:10:35] <lenoch> can you do it in haskell? that just looks weird :p
[20:10:46] <thewormkill> *FIX: the first line has a Just
[20:10:50] <thewormkill> lenoch: yes, you can
[20:10:57] <acaan> well its like c with if statement
[20:11:03] <thewormkill> haskell has a datatype called Maybe a
[20:11:10] <thewormkill> a is any type
[20:11:23] <thewormkill> and Maybe has two value constructors
[20:11:27] <lenoch> yeah I saw that pop around
[20:11:42] <thewormkill> 1. Nothing 2. Just a
[20:11:48] <thewormkill> where a is a value of type a
[20:11:57] <thewormkill> fuck I took the same name for both -.-
[20:12:37] <thewormkill> is that clear that way of should I clarify?
[20:13:10] <acaan> not really
[20:13:25] <acaan> maybe is datatype that we pass two values ?
[20:15:56] <thewormkill> No, Maybe is a datatype that has two constructors
[20:16:04] <thewormkill> Nothing, which takes no arguments
[20:16:13] <thewormkill> and Just x that takes one (x)
[20:16:25] <lenoch> constructors in the oop sense?
[20:16:31] <thewormkill> not exactly
[20:16:48] <thewormkill> a value constructor constructs a value by representing the arguments
[20:17:01] <thewormkill> basically it's a pattern for a value of a type
[20:17:08] <thewormkill> but you can have multiple patterns
[20:17:47] <thewormkill> so, we have a datatype that incorporates failure
[20:18:11] <thewormkill> a function that returns something of type Maybe Int can return:
[20:18:18] <thewormkill> 1. Just <an int here>
[20:18:20] <thewormkill> 2. Nothing
[20:18:36] <thewormkill> but what if we want to chain functions?
[20:18:40] <acaan> ohhh
[20:19:01] <acaan> well it fine if it returns maybe
[20:19:19] <thewormkill> and if the second one *takes* a maybe as argument
[20:19:29] <thewormkill> if it takes an int, we can't do that
[20:19:58] <thewormkill> but there is a concept called a Functor
[20:20:28] <thewormkill> a functor is just this: some construct that contains a value and adds some specific context to it
[20:20:42] <thewormkill> but now comes the interesting part:
[20:21:17] <thewormkill> a functor defines a function fmap that takes a function that takes a type a and a functor with a type a in it
[20:21:32] <thewormkill> and returns a functor with the result of the function in it
[20:21:41] <thewormkill> and it obeys one law
[20:21:44] <thewormkill> if you call
[20:22:07] <thewormkill> fmap identity f where f is an arbitry functor with something inside, you get f back
[20:22:22] <thewormkill> so, that was a lot of info, are there any questions?
[20:22:58] <lenoch> I should learn haskell to understand it fully right?
[20:23:08] <acaan> and math
[20:23:15] <acaan> i mean just enoguh
[20:23:46] <thewormkill> lenoch: well, is there something unclear? because this is just generic atm
[20:24:15] <lenoch> the context thing with a functor
[20:24:29] <lenoch> what do you mean with context?
[20:24:39] <thewormkill> in our example it is failure
[20:25:03] <thewormkill> but it can be basically anything: non-determinism, side-effects...
[20:25:12] <lenoch> oh so a functor is like a return value for a function?
[20:25:26] <thewormkill> no, it's a thing that contains a value
[20:25:34] <thewormkill> and adds some context to it by being thee
[20:25:37] <thewormkill> *there
[20:25:45] <lenoch> anything? or just a function as value?
[20:26:20] <thewormkill> I don't really understand what you mean
[20:26:46] <lenoch> is it like a lambda?
[20:26:54] <thewormkill> no.
[20:27:08] <thewormkill> lists are functors
[20:27:14] <thewormkill> maybe's are as well
[20:27:29] <thewormkill> the idea is that each functor contains values
[20:27:37] <thewormkill> and that it adds context
[20:27:49] <thewormkill> in case of maybe that's the possible absence of a value
[20:28:09] <thewormkill> in case of a list that's the possibly unlimited number of values
[20:28:35] <thewormkill> acaan, Polyphony, do you have questions as well?
[20:29:29] <acaan> hmm, what does functor returns ?
[20:29:45] <Polyphony> honestly i'm just logging this convo so i can look at it later
[20:29:58] <thewormkill> okay, Polyphony
[20:30:05] <thewormkill> acaan: it does not return anything
[20:30:08] <Polyphony> i am a data hoarder, and this is kind of interesting :P
[20:30:16] <thewormkill> it's not a function
[20:30:31] <acaan> its a special haskell construtc ?
[20:30:34] <thewormkill> a list doesn't return anything, doesn't it?
[20:30:35] <acaan> *construct
[20:30:43] <thewormkill> acaan: no, that's category theory
[20:30:55] <acaan> ohh its like wrapper for datatype ?
[20:30:57] <thewormkill> lenoch: is it getting a bit clearer now?
[20:30:59] <thewormkill> acaan: yes
[20:31:05] <thewormkill> absolutely
[20:31:09] <lenoch> ooooh
[20:31:44] <lenoch> Yeah I think it's good
[20:31:52] <acaan> its good now
[20:31:55] <acaan> alot clearer
[20:31:56] <thewormkill> good :)
[20:32:32] <thewormkill> now, we can just fmap functions over functors and thus apply functions to values inside them
[20:32:52] <thewormkill> but now there are some other contexts that are more complex, behod
[20:32:55] <thewormkill> *behold
[20:33:19] <lenoch> in haskel you can in theory chain infinite functions?
[20:33:32] <thewormkill> yes, but that gets messy around 500 ;)
[20:33:56] <lenoch> yeah I mean lol
[20:34:06] <thewormkill> jokes aside, what if our context is "having side effects"?
[20:34:09] <lenoch> seems quite powerfull in some scenario's
[20:34:14] <thewormkill> in all ;)
[20:34:29] <thewormkill> you can split it in parts, which you do
[20:34:32] <lenoch> use a Maybe
[20:34:37] <acaan> it can't have them ?
[20:34:44] <thewormkill> well, but you need them
[20:34:59] <thewormkill> if your program can't print to the screen, you might as well not run it
[20:35:23] <acaan> hmm
[20:35:31] <thewormkill> well, if you need to have a function to have side effects, the values it returns need a context
[20:35:38] <thewormkill> they are "contaminated"
[20:36:06] <thewormkill> I mean if you list a directory, your program needs to know that the data it got could have been different
[20:36:23] <thewormkill> and that it's computation required interacting with teh outside world
[20:37:02] <thewormkill> well, this also requires sequencing
[20:37:20] <thewormkill> let's say you print to the screen and print to the screen again
[20:37:28] <thewormkill> you want the data to appear in order
[20:37:56] <thewormkill> but if all your functions are pure, the compiler can do A LOT of optimizations, rearranging and the like to them
[20:38:10] <thewormkill> so, sequencing is explicit
[20:38:17] <thewormkill> enter monads
[20:38:23] * thewormkill bangs drum
[20:38:43] <thewormkill> a monad is a functor
[20:38:51] <thewormkill> but not each functor is a monad
[20:39:03] <thewormkill> a monad supports a few operations
[20:39:27] <thewormkill> 1. inserting a pure, a context-less value, into a default context
[20:40:17] <thewormkill> 2. taking a value in a monad (a monadic value) and a function that takes a pure value of the same type and returns a monadic value
[20:40:25] <thewormkill> let's express that in code:
[20:40:50] <thewormkill> (>>=) :: Monad m => m a -> (a -> m b) -> m b
[20:40:56] <thewormkill> whoo scary haskell
[20:41:04] <acaan> damn
[20:41:07] <thewormkill> but that's easy: it expresses a type
[20:41:14] <thewormkill> >>= is the function name
[20:41:35] <thewormkill> Monad m means that m is a monad (look up typeclasses if you want to know more)
[20:41:45] <thewormkill> m a is the first argument
[20:41:55] <thewormkill> it's a monad m with a value of type a inside
[20:42:09] <thewormkill> the second argument, (a -> m b) is function
[20:42:17] <thewormkill> now guess what it does
[20:42:40] <acaan> returns monad ?
[20:42:47] <acaan> or functor ?
[20:42:50] <acaan> or b
[20:42:52] <thewormkill> yes it takes an a and returns a monad with b inside
[20:42:59] <thewormkill> * a monad m
[20:43:18] <thewormkill> lenoch: you with us so far?
[20:43:57] * thewormkill throws chalk at lenoch
[20:44:18] <acaan> lol
[20:44:28] <thewormkill> is he asleep?
[20:44:56] <acaan> throw another one
[20:44:57] <Polyphony> heh
[20:45:20] <Polyphony> why in the hell would you name a function >>=
[20:45:33] *** lenoch is now known as lenoch_away
[20:45:41] <Polyphony> inb4 thewormkill is just trolling us all
[20:45:43] <thewormkill> Polyphony: you call it in infix notation
[20:45:55] <thewormkill> arg1 >>= arg2
[20:46:14] <thewormkill> inb4 you ask + is a function in haskell
[20:46:29] <Polyphony> oh lol i get it then
[20:47:33] <thewormkill> hm, not sure if I should carry on without lenoch, he has my last piece of chalk in his beer
[20:47:41] <thewormkill> oh the fucker is away
[20:47:56] <Polyphony> lel
[20:48:37] * thewormkill gets uses a nail to write on
[20:48:46] <thewormkill> okay, he'll catch up I'm sure
[20:49:11] <Polyphony> we're learning shell scripting in OS class... D:
[20:49:14] <thewormkill> well, this way you can chain monadic actions together
[20:49:27] <thewormkill> acaan: are you following at least?
[20:49:30] <thewormkill> ;)
[20:49:40] <Polyphony> acaan is now known as acaan_away
[20:49:42] <acaan> yes
[20:49:52] <thewormkill> good
[20:50:02] <thewormkill> well, now you can add order, how cool is that?
[20:50:38] <thewormkill> there are some more functions defined for monads tho:
[20:50:54] <thewormkill> fail (which just tells us that a monadic action is not possible)
[20:51:17] <thewormkill> and (>>) which has the type
[20:51:32] <thewormkill> (>>) :: Monad m => m a -> m b -> m b
[20:51:49] <thewormkill> it just executes it's arguments in order and returns the second
[20:52:37] <acaan> it returns b ?
[20:52:38] <thewormkill> This way, you can perform IO (although it is buried a bit in the actual implementation of haskell's features
[20:52:49] <thewormkill> acaan: it returns a monad with b inside
[20:53:09] <thewormkill> now, you may ask why we did all this
[20:53:19] <acaan> for no side effects ?
[20:53:38] *** lenoch_away is now known as lenoch
[20:54:03] <thewormkill> yes, you can have complete purity everywhere, unless you need something to perform IO
[20:54:11] <thewormkill> but even those functions are pure
[20:54:14] <thewormkill> you ask why?
[20:54:23] <thewormkill> simple, they just chain monadic actions
[20:54:30] <thewormkill> thus, they return a monad
[20:54:55] <thewormkill> and how that monad does what it does (IO, carrying state, adding read-only data...)
[20:55:06] <thewormkill> is completely independent of it's usage
[20:55:13] <acaan> it wraps it bascicly
[20:55:16] <acaan> it protects purenes
[20:55:17] <thewormkill> yes
[20:55:47] <thewormkill> IO is done by creating IO actions you need and generating the stuff you need to do for them
[20:56:03] <thewormkill> now, you may have noticed that a value can't escape a monad
[20:56:23] <thewormkill> if you put it in, you can transform it etc.
[20:56:27] <thewormkill> but it stays in there
[20:56:34] <thewormkill> because the context can't be undone
[20:56:49] <thewormkill> if you did IO to get a value, that value needs to be treated as such
[20:57:03] <thewormkill> lenoch: you missed a bit, right?
[20:57:13] <lenoch> yeah
[20:58:11] <thewormkill> well, I did essentially explain the basic concept of monads, so you just need to let me know what you missed :)
[20:58:26] <thewormkill> if anyone else has questions, feel free to ask them as well
[20:59:08] <lenoch> hmm no i don't think I missed it
[20:59:12] <lenoch> the bouncer had it
[20:59:26] <thewormkill> oh, nice
[20:59:39] <thewormkill> well, I'll put it up on evilzone, okay?
[20:59:47] <lenoch> good idea!
[21:00:02] * thewormkill is going to get the logs
[21:00:37] <acaan> cool, thanks thewormkill :D
[21:00:41] <thewormkill> np
[21:01:45] <acaan> ok i am starting with haskell now]
Stuff I did: How to think like a superuser, Iridium

He should make that "Haskell"
Quote
<m0rph-is-gay> fuck you thewormkill you python coding mother fucker

Offline Pyromaniac

  • /dev/null
  • *
  • Posts: 19
  • Cookies: 3
    • View Profile
Re: An informal talk about monads
« Reply #1 on: October 16, 2015, 05:20:41 am »
Wow. I am surprise that this log is easier to read than most tuts lol cuz the questions that I was about to ask were asked already by acaan, lenoch, polyphony ;D Now I am curious to try it out.