Open igorbonadio opened 9 years ago
Marking as opened to #18
Marking as opened to #18
Ok. What do you mean by symbols? Is this that ruby feature you mentioned?
Yes. Symbols are like lightweight strings.
I think it is similar to selectors
in objc.
No, i think seletors are a whole different thing hahahaha
Are symbols used just at compile time, or can they me examined at runtime also? For instance, is there any way a function can receive a symbol and print it to the screen?
As far as I know, yes, it's possible. In fact (@igorbonadio can say it much better than I do), many people use them in Ruby as hash keys (instead of strings). The greatest difference between them is that two String
's can be different objects, thought they may have the same value. This leads to confusions as in Java, where we could have:
class PlayingWithStrings
{
public static void main(String[] args) {
String s1 = "Some text";
String s2 = "Some text";
String s3 = s1;
System.out.println(s1 == s2); // => false; different string *objects*
System.out.println(s1.equals(s2)); // => true; equal string *values*
System.out.println(s1 == s3); // => true; references point to the same object
System.out.println(s1.equals(s3)); // => true; equal string *values*
}
}
Symbols solve this problem: in Ruby, any time we reference :symbol
, it will be the same object. It works as a good replacement for C/C++/Java enum
's - instead of declaring for every class in which they would be needed, they are widely available and can be as a selector (perhaps this is a motivation for a name like that in ObjC) in classes.
In Positron, we could use them + match-case
syntax to create factory methods:
def shape_factory(shape : Symbol) -> (res : Shape)
res = match(shape)
case :square => Square(side: 2)
case :rectangle => Rectangle(height: 2, width: 4)
case :triangle => Triangle(base: 2, height: 4)
end
throw Exception("Should be a valid symbol!")
end
This is an indication. I don't remember if we have an otherwise
, case(nil)
or something... Pattern Match spec has nothing about it. Anyway, as a tuple of one object is equivalent to a simple object, I think this would work without changing anything. And using the fact everything is an expression, it's simple to create factory methods like the one above.
What do you think? I'm just not sure how we could insert them in the language. Do we need a special support for :something
such that the compiler will identify it should always reference the same object? For now, I believe so. And I also think :
in the beginning of a symbol is good enough (and has an immediate identification with Smalltalk/Ruby).
Ok, I think I get the basics. However, I wanted to ask: what advantages do symbols have over enums?
I ask that because I think @renatocf's example would be better if we used an enum instead. For one thing, we would know exactly what kinds of shape are allowed: this would mean we could throw a warning if a case had an invalid value (for instance, if there was a typo or if a value had been deleted) and we could enforce cases for all values (so that if a value was added, the compiler warnings could tell the programmer where to change his code to adapt to the new value).
I understand printing symbols could be hefty in debugging, though a good debugger might print enum names too. So, what are the advantages of symbols?
Oh, and "selectors" in ObjC is basically just another word for "methods", it's got nothing to do here :)
Yes. It is like an enum. The only difference is that you dont need to explicitly define the enum type. You can see it as if the compiler could define all possible kinds of enums.
Ah, and you can convert a string to a symbol
"blah".sym == :blah
What I like about enums here is one thing: compiler guarantees. As I understand it, if you have a closed set of enums, there are a lot of advantages that the compiler can bring (like the ones I mentioned above).
However, I can see a few advantages symbols might bring: for example, being able to "build" the symbol from a set of instructions and then use it in code. Personally, though, I'm not really a fan of this "runtime programming" capabilities.
Do you think these two ideas are mutually exclusive? I know they aren't necessarily so, but still, would you want to drop enums if we took on symbols?
Maybe there's a way we can unite both paradigms into one better solution, that might be worth looking into.
In Java, you can find Enum by giving strings. It is a matter of making the compiler inject a symbol table in the compiled code for each enum defined.
@vinivendra is right. I think, for a statically typed language, it is better to have some compiler guarantees. For example:
def shape_factory(shape : Symbol) -> (res : Shape)
res = match(shape)
case :square => Square(side: 2)
case :rectangle => Rectangle(height: 2, width: 4)
case :triangle => Triangle(base: 2, height: 4)
end
throw Exception("Should be a valid symbol!")
end
The shape
definition doesn't express what kind of shapes we can build... Maybe it is better:
enum ShapeType = Square | Rectangle | Triangle
def shape_factory(shape : ShapeType) -> (res : Shape)
res = match(shape)
case ShapeType::Square => Square(side: 2)
case ShapeType::Rectangle => Rectangle(height: 2, width: 4)
case ShapeType::Triangle => Triangle(base: 2, height: 4)
end
end
We could define this enums with parameters:
enum Shape = Square(side: 2)
| Rectangle(height: 2, width: 4)
| Triangle(base: 2, height: 4)
And use it in patter matching:
def area(shape : Shape) -> (res : Int)
res = match(shape)
case Shape::Square(side: s) => s*s
case Shape::Rectangle(height: h, width: w) => h*w
case Shape::Triangle(base: b, height: h) => b*h/2
end
end
The enum would be like a "string with some value".
What do you think?
Those are some really interesting ideas. I think one idea that is worth mentioning is (you guessed it) swift's implementation of enums. Basically, enums would be just like enums in C, except we can specify a type for them. That means they don't have to be integers, they can also be strings, etc. It's an idea that gives the programmer a lot of flexibility.
enum Shape: String
Square = "Square"
Circle = "Circle"
end
enum Bool: Integer
true = 1
false = 0
end
I don't think that "namespace" is necessary though. If we know the value being tested is a Shape, we know exactly what values it can assume - only the ones in the Shape declaration.
Perhaps we can create some special default cases, just as an added sugar. If the enum is defined as an integer or a natural, the values don't need to be specified, they just go from 0 to n. Similarly, if the enum is a String, the values might be the names themselves as Strings.
Lets talk about Symbols