
Imperative Functional Programming (1996) [pdf]  - brudgers
http://www.cs.bham.ac.uk/~udr/papers/imperative-functional.pdf
======
wz1000
In the same vein, [http://blog.jle.im/entry/first-class-
statements](http://blog.jle.im/entry/first-class-statements)

------
eranation
Looking at Rust, Swift, Scala, even JavaScript ES6, I think that Imperative
and Functional styles are doing pretty well together.

~~~
the_af
I can only speak for Scala: it's doing "pretty well", but in some aspects it's
not doing as well as it could, were it a purely functional language.

Scala has a lot going for it, not the least its interoperability with Java.
But being a hybrid language is not zero-cost: its type inference for example
is less powerful than Haskell's (it can infer fewer things; I've read this is
because inheritance complicates things), and to me its type signatures are
harder to read. Also, because pretty much every Scala function has "purity
escape hatches" built-in, you can never be sure what does or doesn't have
side-effects. And this is unrelated to purity, but Scala's null makes me sad.

~~~
tel
The weakness of type inference arises, as you note, due to subtype
polymorphism. You can't escape from this when you've got class-based OO, it
seems.

------
tlarkworthy
[1996]

------
wintom
This is awesome and flies in the face of many arguments that functional and
imperative programming languages need to be kept "pure" and mutually
exclusive.

~~~
coolsunglasses
It'd be great if there was an interface that could be used to abstractly
express the idea of computations that could possibly dependent on "previous"
computations, thereby enabling you to write sequential instructions.

    
    
        (>>=) :: Monad m => forall a b. m a -> (a -> m b) -> m b
    
        Prelude> getLine >>= putStrLn
        blah
        blah
    
        Prelude> do a <- [1, 2]; b <- [4, 5]; if (even a) then (return b) else (return a)
        [1,1,4,5]
    
        Prelude> do a <- Just 1; b <- Just 1; if (even a) then (return b) else (return a)
        Just 1
    
        Prelude> do a <- Just 4; b <- Just 2; if (even a) then (return b) else (return a)
        Just 2
    
        Prelude> do a <- Nothing; b <- Just 2; if (even a) then (return b) else (return a)
        Nothing
        -- depending on a computation that may not have succeeded
    

Then you could distinguish pure and impure code just from the types by
assigning a type wherever effects need to be performed to get our effecty
value.

    
    
        Prelude> :t getLine
        getLine :: IO String
    
        Prelude> :t putStrLn
        putStrLn :: String -> IO ()
    
        Prelude> :t (>>=)
        (>>=) :: Monad m => m a -> (a -> m b) -> m b
    
        Prelude> :t (>>=) getLine
        (>>=) getLine :: (String -> IO b) -> IO b
    
        Prelude> :t (>>=) getLine putStrLn
        (>>=) getLine putStrLn :: IO ()
    

Then we could make a syntax to wrap it all up and let you write code as if it
was actually sequential, imperative instructions.

    
    
        main = do
          putStrLn "Enter your name please: "
          name <- getLine
          putStrLn ("Hello, " ++ name)
    

Maybe we could make a programming language around this, maybe name it after a
mathematician that contributed to the development of lambda calculus.

~~~
Dewie3
It would be great if this hypothetical mechanism didn't have any interpretive
overhead too, for the cases when we need to use it for imperative code for
efficiency reasons.

~~~
asQuirreL
There doesn't _need_ to be an overhead to this extra type disciplining of
side-effectful computing. It is perfectly possible for the compiler to take
the pure monadic code and implement it with actual side effects (which I
believe GHC does do).

What I _do_ miss though (dearly) is that there is a certain cost as to what
can be encapsulated. For instance, in Haskell, it is no longer so easy to hide
side effects behind a pure interface.

~~~
coolsunglasses
>For instance, in Haskell, it is no longer so easy to hide side effects behind
a pure interface.

This is not true. There are a number of ways to do this in Haskell.

Also this is tantamount to, "my programming language won't let me lie", and is
often associated with an infacility with Haskell. I mean no offense, just
speaking from experience.

You don't need to hide effects behind a pure interface once you're comfortable
with the abstractions provided.

Edit: tel's comment is quite good as-is, but I'd like to add that part of what
makes ST nice isn't just having an eliminator, but also so you have a more
precise delineation of what "effects" could happen. IO is kind of a big type.
Still very valuable.

~~~
asQuirreL
So the example I have in mind is that of a Randomised Binary Search Tree (or
Treap, if you want). Its interface is pure, however it requires access to an
RNG for insertions. Because of this, it cannot be treated as a pure
collection: you need to tell the type system, hang on, this RBST carts around
some state which changes when you insert to it, despite the fact that the
state doesn't change the extensional behaviour of the structure. This is a
provable property, but maybe not provable by Haskell's type system.

Now I know that you can hide unsafePerformIO in the interface, and in this
case I expect it is fine, but this is not easy, it feels like a hack.

Another example that I have personally watched play itself out in disbelief
was in a fairly large Haskell project: There was need of a lookup table, with
some 10000 elements. In such cases, I would have stored the lookup in a
database, or a separate file at least, but because they wanted to maintain
purity so badly, the solution was to write a 10000 line long Haskell file. But
wouldn't it be nice if you could load a file and tell Haskell: don't worry,
this won't change, so treat reads to this purely.

I never said such things were impossible, merely not easy, and I don't believe
this is by accident. I am almost certain that such things are made difficult
to prevent abusing such features, but that doesn't stop me from missing them
:) .

