5 ms·
What you describe is exactly what Haskell does. You have pure functions by default, but you can have mutable state wherever you want it--it is just constrained
by tikhonj 14y ago
What you describe is exactly what Haskell does. You have pure functions by default, but you can have mutable state wherever you want it--it is just constrained to a particular scope by the type system. That is, you can use mutable references, mutable arrays and so on all you want inside a computation, and the compiler will guarantee that none of these references leak outside their scope. Haskell accomplishes this with "state threads" (ST[1]).
[1]: http://hackage.haskell.org/packages/archive/base/4.2.0.1/doc/html/Control-Monad-ST.html http://hackage.haskell.org/packages/archive/base/4.2.0.1/doc...
Similarly, Haskell has the IO type for containing any side effects at all. This type, as the name implies, can have not only mutable state but also arbitrary I/O--really anything you like.
This has some very nice advantages: for example, you can create your own wrapper around the IO type which only exposes some IO operations. You could use this for ensuring that plugins can't do things like delete files, for example. (This is one of the example use cases for Safe Haskell[2], which is worth reading about.)
[2]: http://www.haskell.org/ghc/docs/7.4.1/html/users_guide/safe-haskell.html#uses-of-safe-haskell http://www.haskell.org/ghc/docs/7.4.1/html/users_guide/safe-...
You can even reify time explicitly, rather than modelling it implicitly with mutable state. This leads to functional reactive programming (FRP[3]), a new and more declarative way to write things like UIs. FRP replaces event and callback oriented programming, fixing much of the spaghetti code common to most UI frameworks.
[3]: http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming http://stackoverflow.com/questions/1028250/what-is-functiona...
Really, I think "purely functional" is a very unfortunate term for Haskell from a marketing standpoint. "Purely functional" does not mean that we cannot have side-effects, or mutation, or what have you. It just means that side-effects aren't everywhere by default, and don't implicitly permute your otherwise pretty code.
- deleted 14y ago[deleted]
- Chris_Newton 14y agoI appreciate the comment, but what I have in mind looks very different to the way monads are used in Haskell today. For example, I don’t necessarily want pure functions by default in my idealised programming model. To me, a pure function is just a special case of a function whose effects are controlled and where the resources the function might interact with are readily identifiable, for which the sets of possible effects and interacting resources happen to be empty. Put another way, I don’t see anything wrong, or even inferior or undesirable, with performing a quicksort of an array or some fast matrix computations destructively. However, I’d like my language to make sure I couldn’t accidentally try to use the old data again afterwards (including, for example, in other threads running in parallel). I’d like to be sure that just because my algorithm updates one set of data in-place, it doesn’t also draw a window, block to wait for user input, or kick off a background thread that will reformat my hard drive in half an hour. And in the calling code, I’d like to find out very quickly that just because I created and populated an array over here, and then ran a quicksort that changed it over there, and then printed it out down there, nothing else had any chance to modify it in between. And I’d like to do all of that using syntax that is at least as clean and readable as today’s best languages, please, not the horrors that often seem to result when functional programming languages try to implement equivalent behaviour with realistic heavily structured/monadic values instead of the neat one-integer counter or append-only log that always seems to be used in the tutorial code samples. I suspect that to do that will require a language that provides dedicated, specialised tools to support the new programming model. Sure, you could probably do this sort of thing using monads in Haskell, just as you could do OOP in C, text processing in C++, or write in a functional style in Python, but the results are clumsy compared to using the right tool for the job. I’m not sure that tool exists yet for the kind of programming models I’d like to use, but that’s where I’m hoping the lessons of today’s functional languages and academic research will bear fruit over time.
- jberryman 14y agoYou should really take a look at Haskell. What you describe in your 3rd paragraph is precisely the ST monad. It's true we're still pretty limited in how effects are typed (most effectful stuff still ends up in the "IO sin-bin"), but this is an area of active research and experimentation. This is the way haskell's going, and no one else is even close (afaict).
- deleted 14y ago[deleted]
- Chris_Newton 14y agoWhat you describe in your 3rd paragraph is precisely the ST monad. Not exactly. The ST monad supports local mutable state, essentially turning a computation that uses state internally into something that looks pure from the outside. I’m looking for something more general where, for example, a function could modify state outside its own scope, but only if it advertised explicitly which stateful resources it would read and/or write, so the language could enforce constraints or issue warnings if various kinds of undesirable and/or non-deterministic behaviour would result. And really, I want to generalise the concepts of resources and effects on them much more widely. A stateful resource might be a mutable variable, but it could just as well be a file, a database, a single record within a whole database, or anything else I care to model in that way. An effect might be reading or writing part of the state, but it might also be opening or closing a file, or beginning, committing or rolling back a DB transaction. I want a language that can understand the concept that a file must not be read or written before it is opened, or that a database transaction must always be committed or rolled back once it has begun, and warn me if any code paths could permit an invalid order of effects. I don’t just want to throw half my code into the IO monad and hope for the best. I want a language where I can pass a reference to a large data structure into a parallel map function and get a compile-time error because, somewhere in the function I was applying using the map, there was an attempt to perform a mutating operation on that shared data without sufficient safeguards. But I want to write almost identical code and have the effects automatically co-ordinated because the language understands that concurrent access is possible so synchronisation is necessary, but the spec for the parallel map says that non-determinism is permitted. And crucially, I want a language where even simple code that should look like this: def fib(n): if n < 2: return n x, y = 0, 1 for i in range(n - 1): x, y = y, x + y return y does not wind up looking like this (from [1]): fibST :: Integer -> Integer fibST n = if n < 2 then n else runST $ do x <- newSTRef 0 y <- newSTRef 1 fibST' n x y where fibST' 0 x _ = readSTRef x fibST' n x y = do x' <- readSTRef x y' <- readSTRef y writeSTRef x y' writeSTRef y $! x'+y' fibST' (n-1) x y [1] http://www.haskell.org/haskellwiki/Monad/ST http://www.haskell.org/haskellwiki/Monad/ST