Closed Dierk closed 8 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).
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.
• it is mutable not "mutual"
sorry, one of my usual spelling mistakes...
Native-gen supports these 3 cases in types.properties through
pure
,st
andio
. I think we can make the code generator to emit 'mutable native' for types marked withio
.
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
@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.
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
Then how would you name arrays, or StringBuilders, or the likes, which have the following in common:
we can't allow functions like
a[i] = some-value; stringbuilder.append "foo"
in the pure realm.
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.
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
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.
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.
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...