I'm not an expert on Haskell. And this question is not exactly a Haskell question, but I know Haskell people would have a better understanding of what I'm trying to achieve.
So I'm building a dynamic language and I want it to be pure... Totally pure. (With support for IO effects, and I already know why, but that's not part of this question)
Also, I would like it to have some form of polymorphism, so I'm toying with the idea of adding class support.
(Also, everything in the language is supposed to be an expression, so yep, no statements)
While exploring the idea I ended up realizing that in order for it to be referentially transparent, class expressions should be able to be substituted too.
The thing with class expressions is that one of its main functionalities is to check whether some value is instance of it.
So
val Person =class {...}
val person1 =Person(blabla)
Person.instantiated(person1) // returns true
// Equivalent to
class {...}.
instantiated(class{...}(blabla))
Yet! That last part makes no sense... It feels wrong, like I created two different classes
So!
Is there an expression such that
val expr = <<expression>>
expr == expr // true
But <<expression>> == <<expression>>
is false?
In a pure language?
I think that what I'm asking is equivalent to asking if the newtype
Haskell statement could become an expression
CodePudding user response:
The way you've worded your question, you're likely to get at least a few answers that talk about peculiarities of the ==
operator (and, as I write this, you've already gotten one comment to that effect). But, that's not what you're asking, so forget about ==
. Go back to your class example.
Referential transparency implies that after:
val Person = class {<PERSONCLASSDEFN>}
val person1 = Person(<PERSONARGS>)
the two expressions:
Person.instantiated(person1)
and:
(class {<PERSONCLASSDEFN>}).instantiated((class {<PERSONCLASSDEFN>})(<PERSONARGS>))
should be indistinguishable. That is, a program's meaning should not change if one is substituted for the other and vice versa.
Therefore, the identity of classes must depend only on their definition (the part in the curly braces), not where or how many times they are (re)defined or the names they are given.
As a simpler example, you should also consider the implications of:
val Person = class {<CLASSDEFN>}
val Automobile = class {<CLASSDEFN>}
val person = Person(<ARGS>)
val automobile = Automobile(<ARGS>)
after which, the two objects person
and automobile
should be indistinguishable.
CodePudding user response:
I find it difficult to see what this question actually is about, but maybe the problem is that you're talking about equalities when you actually mean equivalence relations?
Two objects that are an instance of the same class are typically not equal, and correspondingly ==
will yield False
. Yet they are equivalent in the sense of being instances of the same class. They're members of the same equivalence class (mathematical term; the usage of the word “class” in both OO and Haskell descends from this).
You can just have that equivalence class as a different operator. Like, in Python
def sameclassinstances(a, b):
return (type(a) is type(b))
Depending on your language's syntax that could of course also be a custom infix operator like
infix 4 ~=.
A separate issue is that equality itself can be interpreted as either value equality (always in Haskell), or some form of implementation equality or reference equality, which is fairly common in other languages. But if you want your language to be pure, you should probably stay away from the latter, or give it a telling name like Haskell's reallyUnsafePtrEquality
.