Frege / frege-native-gen

Frege code generator for Java classes
17 stars 9 forks source link

Supporting the "mutable native" style #14

Closed Dierk closed 8 years ago

Dierk commented 9 years ago

It appears that there are two styles in declaring a typical "PoJo" - where the style that uses the "mutable native" declaration seems to be favored since recently. data MyPojo = mutable native my.package.MyPojo where...

Ingo60 commented 9 years ago

Intermission from Sender Yerewan:

In principle yes, but ....

pure native is immutable, prototypical are java.lang.String or BigInteger.

native is mutable, but can also be useful if we can "freeze" it or extract an immutable value. Prototypical are StringBuilder and all kinds of arrays. Can appear in "pure native" methods or ST actions. In the latter ones, it may only appear as wrapped in (Mutable s), however, where s is the phantom type of the ST monad.

mutable native can never be regarded as immutable, and its methods are typically concerned with IO. Prototypical is FileOutputStream, or GUI elements. Because this is so, the wrapping in (Mutable RealWorld) is redundant.

The hard part is to decide in which of those 3 classes does a java class fall? An easy solution would be to regard them all as mutable native, but this would mean that we must write mostly IO-monadic code. Another, and even more general approach would be to never use mutable native, and wrap every appearance of the type name in (Mutable RealWorld).

mmhelloworld commented 9 years ago

Native-gen supports these 3 cases in types.properties through pure, st and io. I think we can make the code generator to emit 'mutable native' for types marked with io.

On Wednesday, March 4, 2015, Ingo Wechsung notifications@github.com wrote:

Intermission from Sender Yerewan:

In principle yes, but ....

  • it is mutable not "mutual"
  • It is not a question of favored. Look, we support 3 classes of native data:

pure native is immutable, prototypical are java.lang.String or BigInteger.

native is mutable, but can also be useful if we can "freeze" it or extract an immutable value. Prototypical are StringBuilder and all kinds of arrays. Can appear in "pure native" methods or ST actions. In the latter ones, it may only appear as wrapped in (Mutable s), however, where s is the phantom type of the ST monad.

mutable native can never be regarded as immutable, and its methods are typically concerned with IO. Prototypical is FileOutputStream, or GUI elements. Because this is so, the wrapping in (Mutable RealWorld) is redundant.

The hard part is to decide in which of those 3 classes does a java class fall? An easy solution would be to regard them all as mutable native, but this would mean that we must write mostly IO-monadic code. Another, and even more general approach would be to never use mutable native, and wrap every appearance of the type name in (Mutable RealWorld).

— Reply to this email directly or view it on GitHub https://github.com/Frege/native-gen/issues/14#issuecomment-77199497.

Dierk commented 9 years ago

• it is mutable not "mutual"

sorry, one of my usual spelling mistakes...

Dierk commented 9 years ago

Native-gen supports these 3 cases in types.properties through pure, st and io. I think we can make the code generator to emit 'mutable native' for types marked with io.

that sounds good to me

If I now have understood it correctly, the "stylistic" issue is only about using "mutable native" once or (Mutable RealWorld) for the return type of every method.

native is mutable, but can also be useful if we can "freeze" it or extract an immutable value. Prototypical are StringBuilder and all kinds of arrays. Can appear in "pure native" methods or ST actions. In the latter ones, it may only appear as wrapped in (Mutable s), however, where s is the phantom type of the ST monad.

mutable native can never be regarded as immutable, and its methods are typically concerned with IO. Prototypical is FileOutputStream, or GUI elements. Because this is so, the wrapping in (Mutable RealWorld) is redundant.

I guess I was a bit misled by the naming, since as it appears the difference between "native" and "mutable native" is not the mutability but the phantom type (s vs RealWorld).

I translate this for myself as native -> somehow mutable mutable native -> IO mutable

So as it seems that native implies mutability anyway, why having an extra word for that? If "mutable native" implies IO, it may be easier to understand in the form of "native io".

just my 2 cents

cheers Dierk

Ingo60 commented 9 years ago

@Dierk I'd think of it more as

pure native - never mutable native - may be mutable mutable native - always mutable

You're right insofar as there is the theoretical possibility of a technically immutable value, that nevertheless is always used in IO related context - like a POSIX file handle (basically a small integer) that is used in file operations. And still, we would prefer this to be "mutable native". Why? Because then you can't define accidentally a pure native function that operates on it.

Dierk commented 9 years ago

native - may be mutable

this is the one that I don't understand, especially in the presence of "pure native", where in contraposition, this can only stand for

One could argue that pure implies immutable (but not the other way around as you mentioned) , that "pure immutable" would be redundant, and "pure" is shorter. Second, the lack of "pure" or the lack of "mutable" can imply "impure" or "immutable" respectively. This is where the uncertainty creeps in, IMHO. I'm not even sure if this is so at the moment.

I'm certain that I don't oversee all the effects that these declarations have but to me the whole purpose of these declarations is to be explicit. Therefore, I would prefer an explicit naming.

For the shortcut notation I would always give both properties

(im)pure (im)mutable native

cheers Dierk

Ingo60 commented 9 years ago

Then how would you name arrays, or StringBuilders, or the likes, which have the following in common:

And still, when the array or stringbuilder is filled, and we did create that thing ourselves or made a private copy of something passed in, we can henceforth regard it as read-only, and allow pure functions on it.

Take the following:

join3 a b c = ST.run (stjoin a b c) where
    stjoin a b c = do
        sb <- StringBuilder.new -- ST s (Mutable s StringBuilder)
        sb.append a    -- Mutable s StringBuilder -> String -> ST s ()
        sb.append b
        sb.append c
        sb.toString   -- toString :: Mutable s Stringbuilder -> ST s String

Why should the overall function join3 not be pure here? The only concern that we have is that the native value must not be taken for a pure one. This is so in this case (the sb is simply garbage collected, after we extracted the final string).

The same is it in the case of the arrayFromList functions: the array is created and filled in one go, and then frozen and returned. Why should this not be possible, when "foo" ++ "bar" is? You know what is happening there? An array of 6 characters is allocated and filled with 'f' 'o' 'o' 'b' 'a' 'r' and then frozen and wrapped inside a String object, which is returned. The point is that the memory manipulation is not observable, and hence (++) (or rather, the Java operator + on Strings) is pure in a language without object identities.

Dierk commented 9 years ago

sorry, Marimuthu for hijacking your project issues here :-)

Then how would you name arrays, or StringBuilders, or the likes, which have the following in common:

We can at any time opt out of the shortcut notation and use explicit ST s (Mutable s StringBuilder) vs ST s (Mutable Realworld StringBuilder) and so on.

As I understood it, the shortcut notation is for the simple cases - typical pojos that may be immutable (like data transfer objects) getter-setter style beans (change internal state but that is all they do) full beans (with listeners on them etc.; "let go all hope")

The benefit of the shortcut notation (as I understood it so far) is to provide some classification that makes it easier to understand what kind of class we're dealing with.

cheers Dierk

Ingo60 commented 9 years ago

We can at any time opt out of the shortcut notation

Sure, but it's clumsy precisly in those cases when you know in advance that you have to write

Mutable RealWorld Thing

all the time. So, in this case, everybody would do the following

type IOThing = Mutable RealWorld Thing

or, maybe even

data ClumsyLabel = native javafx.components.Label where ...
type Label = Mutable RealWorld ClumsyLabel

and then you're back to nice type signatures like:

native setText :: Label -> IO ()

So, the name ClumsyLabel would have been defined only (as far as human readers are concerned) to not being used at all, except for the type declaration.

And then consider the following: someone imports the following module:

data ClumsyLabel = native javafx.components.Label where
    new :: String -> IO Label
data FancyLabel = native javafx.components.FancyLabel
native setFancyShadow :: Mutable RealWorld FancyLabel -> IO () 
type Label = Mutable RealWorld ClumsyLabel

and makes the following error because he doesn't realize that fancy shadows are only possible for FancyLabels:

do 
      label <- Label.new "Amazing"
      setFancyShadow label

and gets the error message

type error in expression label
    type is ClumsyLabel
    used as FancyLabel

Yet the user was not even aware of the existence of ClumsyLabels. So this is going to get confusing in the utmost way.

The Mutable s apparatus makes only sense when there is a remote possibility that the type can be used in pure native functions, and not only in ST actions. When this is out of the question, it is redundant, and redundancies ought to be avoided.

That being said, I don't claim that the way how the different classes of native types are introduced is superior. We can change the keywords for their definitions, but the classes themselsves persist (I didn't invent them, they're just there, you know!), and also the Mutable s machinery will only be needed for the second class, and not enforced when it is redundant.

mmhelloworld commented 8 years ago

I am closing this issue now as the 3 "styles" of mutability are already supported via pure, st and io in types.properties. Please reopen if there is still an issue.