Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon.

Pages: 1-

Simple Side Effect Control Suggestion

Name: Anonymous 2014-08-30 20:22

SSECS
BUTTSSECS

Anyway, here's a suggestion: how about we scrap the monads... okay-okay, monads are useful, but not for side effects control.

How about we take side effect control from the type system (which is poor at it) and move it to a separate layer of metadata? You've got several effectful language primitives and they're all "flagged", with the flags propagating to the caller and to the caller's caller and so on. E.g. mutation is done via the "set" primitive:

(set foo bar) ; gets flagged with {modifies FOO}

so when you've got a function that mutates something, that function gets the flag:

(def (mutator x) ; gets flagged with {mutates X}
(progn
(set x (+ x 1))
x))


and then

(def (iomutator x) ; gets flagged with {IO, mutates X}
(let ((y (mutator x)))
(print "Y is " y "X is " x)))


Then when you're defining a function, you can express constraints on the effect flags of its parameters:

(def (map f xs)
(:: (-> a b) (list a) (list b))
(:#: f {not IO, not mutates *})
;; definition of map)


But then - get this - you can compile in an effects-permissive mode. You can make the compiler allow calls of (map f xs) with effectful f (as long as it typechecks). Or you could make them compile-time errors. Because static reasoning about effects is immeasurably harder than reasoning about values, effect-checking should be optional while type-checking should be mandatory. So if you want effects to run galore, you flip the "effect-permissive" compiler option, and if you want to compile some untrusted code from some shithead, you flip the "effect-strict" option.

Name: Anonymous 2014-08-30 20:39

So what advantages do we get?

Composability. Monads do not compose in general, and monad transformers are shit. With this way, effects compose just as easily as in any imperative language and you don't have to lift anything or worry if your monad stack is in the right order.

Flexibility. Side effects cannot be reasoned about statically. If you're writing to a file that is watched by a daemon that launches missiles when that file gets written to, then you're in fucking trouble - but what if you're just dumping some debug output to logs? It is for this reason that Haskellers have to use unsafePerformIO and shit. With my scheme, you can just write a compiler pragma or flip a compile option - et voila, your effectful yet trusted code will compile just fine as long as it typechecks.

It keeps us honest. Haskell doesn't, because it has (IO a -> a). With my proposal, you can never hide any effects. Every function will be ruthlessly flagged, so just by looking at its header, you will know what it mutates, to what DBs it makes transactions, etc. No cheating like in Haskell.

Informativity. The very purpose of side effects control is to make side effects expicit and checkable. How fucking informative is the fact that a function returns (ST s Int)? Ooh, that function might mutate something. Or it might not. With my system, you'll have a return type of just int, but also a bunch of flags like {mutates someVar, mutates someOtherVar, transacts fuckDB}. So when you have some hard-to-track bug with some mutable state, the IDE will be able to offer you the whole list of functions that mutate this particular assignable.

Fuck, this is so simple and powerful. Why doesn't anyone make a language like that?

Name: Anonymous 2014-08-30 21:57

Implement a Haskell2010-compliant langauge that has this.

Name: Anonymous 2014-08-31 1:43

It's impossible to do anything more than fibs in Haskell without cheating. If you make cheating more difficult, you just doing interesting things more difficult.

That said, I still like this idea, but I will have to contemplate it for a while.

Name: Anonymous 2014-08-31 6:01

>>4
No cheating only means that effects cannot be hidden like in Haskell:

foo :: Int -> Int
foo x = unsafePerformIO $ do
print "HASKAL IS NOT PURE"
return (x + 1)


If a function does something, it will have the flag. However, whether effects mismatch will be a compile-time error depends on compiler options/pragmas. If you want the compiler to do stream fusion on effectful traversals, changing the semantics - it will do just that. But if you want it to be strict, then it will halt compilation in such cases.

My key idea is that effects should not be managed through the type system, because they cannot be reasoned about statically in an unambigious way. Addition of an int and a float is a type error and should always be prevented - we know that statically, and this fact does not depend on anything. But is mutation of shared state good or bad? Can't know that statically.

Name: Anonymous 2014-08-31 10:18

If a function does something
What would be the point of a function that doesn't do anything?

Name: Anonymous 2014-08-31 10:34

>>6
It would just return a value. Which is plenty useful.

Name: Anonymous 2014-08-31 10:46

Finely-grained constraints. Instead of the one-size-fits-all distinction "in the IO monad/not in the IO monad", the possible constraints on effects would become more varied and useful. For example:

(def (foo worker-func mutable-state)
(:: (-> int int) (state-type))
(:#: worker-func {not mutates mutable-state})
;; definition of foo)


Here we don't give a shit if worker-func launches missiles or posts naked pics of you on tumblr, we only care that it should never mutate the mutable-state assignable. With this constraint, the compiler would check every call site and make an error if somewhere foo is passed a badly-behaving worker-func. Of course we could also flip the switch and make the compiler ignore all that.

Name: Anonymous 2014-08-31 10:49

>>8
I'm sorry, the type signature of foo should be (:: (-> int int) (state-type) unit), but that's beside the point (which is effects, not types).

Name: Anonymous 2014-08-31 13:15

>>7
But returning a value is doing something.

Name: Anonymous 2014-08-31 14:09

>>10
Values are easily controlled via type systems. It is the side effects that need a simple and powerful way of controlling. The Haskell way is simple but very weak, while dependent types are powerful yet overwhelmingly complex and unwieldy.

Name: Anonymous 2014-08-31 14:20

DO YOU CALL THIS A CHICKEN??!??! http://wiki.call-cc.org/chicken-small.png IT'S SHIET!!

Name: Anonymous 2014-08-31 14:30

>>12
Yep, does indeed look like shit-wipes.

Name: Anonymous 2014-08-31 15:18

Name: Anonymous 2014-08-31 15:38

>>14
Slava!

Name: Anonymous 2014-08-31 17:20

>>15

aleikhem shalom!

Name: Anonymous 2014-09-01 16:54

>>14
jesus

Name: Anonymous 2014-09-01 19:15

>>17
anus

Don't change these.
Name: Email:
Entire Thread Thread List