[Haskell-cafe] Investing in languages (Was: What isyourfavouriteHaskell "aha" moment?)

PY aquagnu at gmail.com
Mon Jul 16 07:44:49 UTC 2018


> So I think if you don't see anybody explicitly mentioning spaghetti 
> issues with State that's for some people it's just hiding in plain 
> sight and they either aren't consciously aware of it, or find that 
> area so self-explaining that they do not think they really need to 
> explain that.
>
IMHO State monad solution is orthogonal to my point. It does not force 
you to isolate state change in one place with explicit control, it only 
marks place where it happens. This info is needed to compiler, not to 
me. For me - no benefits. Benefit to me - to isolate changing, but with 
State I can (and all of us do it!) smear change points throughout the 
code. So, my question is: what exact problem does solve State monad? 
Which problem? Mine or compiler? Haskell pure lambda-only-abstraction 
limitation? OK, if we imagine another Haskell, similar to  F#, will I 
need State monad yet? IMHO - no. My point is: State monad is super, in 
Haskell, and absolutely waste in other languages. I will isolate 
mutability in another manner: more safe, robust and controllable. Recap:
1. State monad allows you to mark change of THIS state, so you can easy 
find where THIS state is changing (tracking changes)
2. Singleton with FSM allows you to *control* change and to isolate all 
change logic in one place

1st allows spaghetti, 2nd - does not. 2nd force you to another model: 
not changes, but change requests, which can return: "not possible". With 
Haskell way the check "possible/not possible" will happen in locations 
where you change state in State monad: anywhere. So, my initial point 
is: State monad is about Haskell abstraction problems, not about 
developer problems.

> Sorry, but that's not what OO is about.
> Also, I do not think that you're using general FSMs, else you'd be 
> having transition spaghetti.
To be precise, then yes, you are right. But such model forces me more, 
then monadic model. When you create singleton "PlayerBehavior", and have 
all setters/getters in this singleton and already check (in one place!) 
changes - next step is to switch from checks to explicit FSM - in the 
same place. Haskell nothing offers for this. You *can* do it, but monads 
don't force you and they are about Haskell problems, not mine. 
Motivation of State monad is not to solve problem but to introduce state 
mutability in Haskell, this is my point. OK, State monad has helpful 
side-effect: allows to track change of concrete THIS state, but I can do 
it with my editor, it's more valuable to Haskell itself, then to me, 
because no problem to mutate state: Haskell allows it, Haskell also does 
not guard you to mutate this state anywhere in the application.

I'm agree with you 100%. My point is related to accents only, my thesis 
is: monads have value, but it's small, it's justified in Haskell with 
its limitation to one abstraction, but I don't need monads in other 
languages, their value in other languages is super-small (if even 
exists). So, motivation of monads introduction (for me, sure, I'm very 
subjective) is to workaround Haskell model, not to make code more safe, 
I'm absolutely sure: monads nothing to do with safety. It's like to use 
aspirin with serious medical problem :)

> Let me repeat: What you call a "message" is just a standard 
> synchronous function call. The one difference is that the caller 
> allows the target type to influence what function gets actually 
> called, and while that's powerful it's quite far from what people 
> assume if you throw that "message" terminology around. 
I mentioned Erlang early: the same - you send message to FSM which will 
be lightweight process. Idea of agents and messages is the same in 
Smalltalk, in QNX, in Erlang, etc, etc... So, "message" does not always 
mean "synchronous call". For example, QNX "optimizes" local messages, so 
they are more lightweight in comparison with remotely messages (which 
are naturally asynchronous). But "message" abstraction is the same and 
is more high-level then synchronous/asynchronous dichotomy. It allows 
you to isolate logic - this is the point. Haskell nothing to do with it: 
you smear logic anywhere. But now you mark it explicitly. And you have 
illusion that your code is more safe.

> But that's not the point. The point is that Haskell makes it easy to 
> write non-spaghetti.

How? In Haskell I propagate data to a lot of functions (as argument or 
as hidden argument - in some monad), but with singleton+FSM - you can 
not do it - data is hidden for you, you can only *call logic*, not 
*access data*. Logic in Haskell is forced to be smeared between a lot of 
functions. You *CAN* avoid it, but Haskell does not force you.

> BTW you have similar claims about FSMs. Ordinarily they are spaghetti 
> incarnate, but you say they work quite beautifully if done right.
> (I'm staying sceptical because your arguments in that direction didn't 
> make sense to me, but that might be because I'm lacking background 
> information, and filling in these gaps is really too far off-topic to 
> be of interest.)

I respect your position. Everybody has different experience, and this is 
basically very good!

> We often repeat this: “side-effects”, “tracks”, “safe”. But what does 
> it actually mean? Can I have side-effects in Haskell? Yes. Can I mix 
> side-effects? Yes. But in more difficult way than in ML or F#, for 
> example. What is the benefit?
>
> That it is difficult to accidentally introduce side effects.
> Or, rather, the problems of side effects. Formally, no Haskell program 
> can have a side effect (unless using UnsafeIO or FFI, but that's not 
> what we're talking about here).

Actually if we look to this from high-level, as to "black box" - we see 
that it's truth. Haskell allows to have them, to mix them but in 
different manner.

> Yes they will. Some tests will fail if they expect specific output. If 
> the program has a text-based user interface, it will become unusable.

And wise-versa: if I will remove "print" from such tests and add "pure" 
- they can fail too. IMHO purity/impurity in your example is related to 
expected behavior and it violation, not to point that "more pure - less 
bugs". Pure function can violate its contract as well as impure.

> Yes they will become buggy. You'll get aliasing issues. And these are 
> the nastiest thing to debug because they will hit you if and only if 
> the program is so large that you don't know all the data flows 
> anymore, and your assumptions about what might be an alias start to 
> fall down. Or not you but maybe the new coworker who doesn't yet know 
> all the parts of the program.
> That's exactly why data flow is being pushed to being explicit.

So, to avoid this I should not mix read/write monads, to avoid RWST. In 
this case they should be removed from the language. And monad 
transformers too. My point is: there is some misunderstanding - I often 
listen "side-effects are related to errors", "we should avoid them", 
"they leads to errors", etc, etc, but IMHO pure/impure is needed to FP 
language compiler, not to me. This is the real motto. Adding of 
side-effects does not lead to bugs automatically. Mostly it does not. 
More correct is to say: distinguish of pure/impure code is better to 
analyze the code, to manipulate with it, to transform it (as programmer 
I can transform F# code *easy because no monads*, in Haskell *compiler* 
can transform code easy *because monads*). More important argument for 
me is example with Free monads. They allows to simulate behavior, to 
check logic without to involve real external actions (side-effects). 
Yes, OK, this is argument. It's not explicitly related to buggy code, 
but it's useful. It remember me homoiconic Lisp code where code can be 
processed as data, as AST.

Actually, I had a big interesting discussion in my company with people 
which does not like FP (the root why I become to ask such questions to 
himself). And I got their arguments. I tried to find solid base of mine. 
But currently I see that I like Haskell solutions itself, and I can not 
show concrete examples where they are needed in real world, without 
Haskell specific limitations. I know that those limitations lead to slow 
compilation, to big and complex compiler, I can not prove that 
side-effects means "lead to error", or (more interesting) that it's bad 
to separate side-effects from each other. F#, ML, Lisps have "do-" block 
and no problem with it. They don't need transformers to mix 2 different 
effects in one do-block. If you can prove that this decision leads to 
bugs and Haskell solution does not: it will be bomb :) I think, will be 
a lot of people in CS which will not agree with you ever.

---
Best regards, Paul



More information about the Haskell-Cafe mailing list