3 ms·
I see where you're coming from, but I wouldn't say it's different than "thread it through everything". The monad way is to pass a blob through a pipeline and us
by camgunz 5y ago
I see where you're coming from, but I wouldn't say it's different than "thread it through everything". The monad way is to pass a blob through a pipeline and use types to say "this thing I'm threading through the pipes is an integer, but also a logger". The convenience here is that the extra state is attached to your data, so you don't have to explicitly thread it through each function. But for that convenience, you're (almost always) using up memory or suffering dereferencing (or both).
This dichotomy is fundamental, which is why I'm sticking on it here. Either you have to deliberately hand this piece of data down into the scope you're working with, or you can summon it from any scope. There really are no other options.
- AnimalMuppet 5y agoI see your point about "threading it through". But doesn't that mean that you're agreeing with me that OO and monads are doing the same thing (at least in this regard)? And, can someone explain to me how the type system works here? I had thought that an advantage of monads were that you just had to change the type signature to take a monad rather than to take an int, but once you did that, it could take any monad. But that won't work, will it? If you're going to try to log from that function, then it has to be a monad that logs, doesn't it? It can't just be a generic monad; it has to be a specific one.
- camgunz 5y agoOh, yeah I like this. Yeah we definitely agree. Re: type system, AFAIK you do like (let's assume something Go-ish here): type Integer interface { GetValue() int Mul(i Integer) Integer } type Logger interface { LogMessage() } type DataBlob struct { value int message string } func (db *DataBlob) GetValue() int { return db.value } func (db *DataBlob) Mul(i Integer) Integer { db.value *= i.GetValue() return db } func (db *DataBlob) LogMessage() { log.Println(db.message) } func triple(db *DataBlob) *DataBlob { return db.Mul(&DataBlob{3, ""}) } --- In this way, you can always implement more interfaces onto DataBlob, and then modify your pipeline steps to take advantage of the new functionality. This example isn't really perfect, but it's reasonably illustrative. The thing is getting the compiler to know you can do different things with a given piece of data. In Go that's interfaces, in Haskell that's typeclasses, in Rust that's traits, blah. P.S. This [0] Rosetta Code example made it pretty clear to me, so if you're the same kind of thinker maybe it'll help you. [0]: https://rosettacode.org/wiki/Monads/List_monad#Go https://rosettacode.org/wiki/Monads/List_monad#Go