2

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

caeus
  • 3,084
  • 1
  • 22
  • 36
  • 1
    `0/0 == 0/0` is the canonical example in any language with FloatingPoint support; since `NaN` isn't equal to itself according to IEEE-754 semantics that Haskell follows. – alias Nov 19 '21 at 18:35
  • 1
    It depends on what you're signifying by `==`. When I read "==", I expect it to have the properties of an equivalence relation. One of the basic properties of an equivalence relation is reflexivity: `a == a` for every element `a` of the underlying set. – Asad Saeeduddin Nov 19 '21 at 18:39
  • Are you perhaps saying that _two_ distinct expressions `a` and `b` might evaluate to an identical _value_? – Asad Saeeduddin Nov 19 '21 at 18:42
  • “The thing with class expressions is that one of its main functionalities is to check whether some value is instance of it.” _UH!_ I mean, yes this does happen a lot in practice, but many people would argue that's it's completely counter to the spirit of proper OO. — Anyway I don't really see how this relates to the question... – leftaroundabout Nov 19 '21 at 18:53
  • Gotta admit it's a difficult question to ask... I struggled wording it, just as much as I'm struggling finding an answer to it – caeus Nov 19 '21 at 20:06
  • You are asking about `==`. But with equality, one must be *very* careful about what one means. For example, in Haskell, there is a *function* named `(==)`, and there's a concept of "actual equality", and there's no part of the language spec that guarantees those align with each other. I suspect that if you try to work out which equality you're asking about, you'll instantly have the answer as "yes, trivially and obviously" or "no, trivially and obviously". The only reason there is confusion about the answer is because there is confusion about the question. – Daniel Wagner Nov 19 '21 at 20:08

2 Answers2

4

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.

K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71
  • Exactly my thinking. So creating a class, the way I want, would be an effectful operation – caeus Nov 19 '21 at 20:04
  • @Buhr... I've come with two possible solutions, which could ruin the "purity" of the language. 1. Identity of that expression depends on the position of the definition (as in... Position in the sourcecode) and 2. Exactly what you say, but every class inherits some extra identifier from the file in which it was written. So it'd work the way you say it. But I would be unable to write the same class in another file. Even if I copy it exactly – caeus Nov 19 '21 at 20:08
  • 1
    If the syntax was `class Person {...}` then the name can be part of the value (or you could do something like `class { name = Person, ... }`, whatever floats your syntactic boat). That way an object is at least much less likely to be a member of somebody else's random class that only happens to have the same definition. You can "manually" keep track of names of classes even without it baked into the syntax (e.g. by using tuples of a string and a class), but putting it into the syntax *forces* everyone to give their classes a name. – Ben Nov 19 '21 at 23:08
0

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.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319