|
|
|
|
|
|
| |
| |
|
|
|
|
| |
| |
|
|
I realise this is a radical and controversial thing to say, but it is. :-P
Suppose, for example, that I wanted to implement a lambda expression
interpreter. For those who don't know, a lambda expression has one of
three possible forms:
1. A variable name.
2. A lambda function. This consists of a lambda, followed by a
variable name, followed by an arrow, followed by any valid lambda
expression.
3. A function application. This consists of any two valid lambda
functions, separated by an empty space.
Usually brackets are used to disambiguate when such expressions are
written as text. But conceptually, the above exactly describes the form
of a lambda expression.
Now, suppose we want to define a data structure for storing such
expressions, and a way to convert one into text. (Converting /from/ text
is obviously much harder.)
In Haskell:
data Expression =
Variable String |
Lambda String Expression |
Apply Expression Expression
deriving Show
This defines our data structure. The final line tells the compiler to
auto-generate the code for conversion to text. However, if you don't
like the way the compiler's auto-generated code does it (and we don't,
in this case), it's not hard to write it yourself:
lambda_text :: Expression -> String
lambda_text e =
case e of
Variable n -> n
Lambda n e -> "λ " ++ n ++ " → " + lambda_text e
Apply e f -> lambda_text e ++ " " ++ lambda_text f
In reality, you /probably/ want to make it insert some brackets and
stuff, but the above is the gist of it. If we remove the "deriving"
line, we have a total of 10 lines of code here.
Now let's try the same thing in Java:
public abstract class Expression {}
public class Variable extends Expression
{
private String Name;
public Variable(String n) {this.Name = n;}
public String toString() {return this.Name;}
}
public class Lambda extends Expression
{
private String Variable;
private Expression Body;
public Lambda(String v, Expression b)
{
this.Variable = v;
this.Body = b;
}
public String toString()
{
return "λ " + this.Variable.toString() + " → " +
this.Body.toString();
}
}
public class Apply extends Expression
{
private Expression LHS, RHS;
public Apply(Expression l, Expression r)
{
this.LHS = l;
this.RHS = r;
}
public String toString()
{
return this.LHS.toString() + " " + this.RHS.toString();
}
}
If your screen is anything like mine, that lot doesn't even fit on one
page. This is /a lot/ of code. And yet it doesn't even "do" anything, as
such. It just lets you construct data structures, and print them out.
The same thing as the 10 lines of Haskell code did. I count 44 lines.
Or, if you're feeling generous, 36 non-blank lines. And I even tried to
scrunch Lambda.toString() onto one line, even though it's really too big.
Now suppose that, for some strange reason, I want to be able to check
whether two expressions are the same. (Realistically, I can't think why
you'd want to do that for anything except variables.) In Haskell, I can
simply append "deriving Eq", and the compiler will write the code for me
in the obvious way. In this instance, it does the right thing. In
Java... well OK, I'll go easy on Java and only write the code for
Variable. Here goes:
public class Variable extends Expression
{
private String Name;
public Variable(String n) {this.Name = n;}
public String toString() {return this.Name;}
public boolean equals(Object obj)
{
if (obj == null) return false;
if (obj instanceof Variable)
{
Variable v = (Variable) obj;
return this.Name.equals(v.Name);
}
return false;
}
public int hashCode() {return this.Name.hashCode() + 9;}
}
OK, so only 14 lines of actually new code. The hashCode() override is
not /strictly/ necessary, but the IDE whines like hell if you don't, and
it's highly likely that you're going to put variables as keys into a
HashMap at some point, in which case hashCode() had better work correctly!
In Haskell, you can simply say
e `equals` f =
case (e, f) of
(Variable x, Variable y) -> x == y
and you're done.
In Java, you have to check whether the argument is null. (Impossible in
Haskell.) You have to check whether the argument is of a comparable
class. (Haskell's static type checking does that for you at
compile-time. Actually Java's generics could do that too if they
extended it a teeny weeny bit... but they haven't.) Then you have to
actually type-cast it. (Unnecessary in Haskell due to static checking.)
Then, and only then, can you do the actual test. But because it's Java
and it sucks, you can't just say this.Name == v.Name; that would do a
pointer comparison, not a string comparison. So you must instead do
this.Name.equals(v.name), which is obviously far more readable. :-P
On the plus side, the IDE will generate some of this code for you. On
the minus side, you still have to /read/ it. And perhaps the worst thing
about the above Java code isn't that it's 44 lines, but that it's in 4
separate files, when /obviously/ it's notionally a single entity. (Then
again, I think if you make the classes private - excuse me, "package
scope" - then they may all be put in one file.)
In consequence to the above, I just spent an entire morning coding in
Java, and all I actually have to show for it is a boat-load of classes
which can hold data and generate a graphical representation of
themselves. They have no actual /behaviour/ yet. It's taken me this long
just to do that... Man, Java is a blunt knife! (But oh so hard to hurt
yourself with... yeah, right!)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 10/04/2012 01:10 PM, Invisible wrote:
> OK, so only 14 lines of actually new code. The hashCode() override is
> not /strictly/ necessary, but the IDE whines like hell if you don't, and
> it's highly likely that you're going to put variables as keys into a
> HashMap at some point, in which case hashCode() had better work correctly!
Also, the IDE complains if you don't write "@Override" in front of every
method that overrides an inherited one. I don't know why. (And I'm not
actually sure how that's valid Java syntax, come to mention it...)
This /does/ have the nice side-effect that if you rename the method in
the superclass or something, the IDE starts yelling "hey, this says
@Override, but it doesn't!"
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Em 10/04/2012 09:10, Invisible escreveu:
> Suppose, for example, that I wanted to implement a lambda expression
> interpreter.
I don't think most java programmers would want that. :p
but kudos for installing and configuring netbeans just for that... :)
> In reality, you /probably/ want to make it insert some brackets and
> stuff
you really can't spell parenthesis, huh? ;)
> , but the above is the gist of it. If we remove the "deriving"
> line, we have a total of 10 lines of code here.
>
> Now let's try the same thing in Java:
>
> public abstract class Expression {}
>
> public class Variable extends Expression
> {
> private String Name;
>
> public Variable(String n) {this.Name = n;}
>
> public String toString() {return this.Name;}
> }
>
> public class Lambda extends Expression
> {
> private String Variable;
> private Expression Body;
>
> public Lambda(String v, Expression b)
> {
> this.Variable = v;
> this.Body = b;
> }
>
> public String toString()
> {
> return "λ " + this.Variable.toString() + " → " + this.Body.toString();
> }
> }
>
> public class Apply extends Expression
> {
> private Expression LHS, RHS;
>
> public Apply(Expression l, Expression r)
> {
> this.LHS = l;
> this.RHS = r;
> }
>
> public String toString()
> {
> return this.LHS.toString() + " " + this.RHS.toString();
> }
> }
>
> If your screen is anything like mine, that lot doesn't even fit on one
> page. This is /a lot/ of code.
Java is very obviously verbose, and it doesn't help when people even go
further and write damn "public" all over in every occasion!
class Apply? Really? The name itself and the behaviour conveying an
action shouldn't be best served as methods of Expression?
> And yet it doesn't even "do" anything, as
> such. It just lets you construct data structures, and print them out.
> The same thing as the 10 lines of Haskell code did. I count 44 lines.
> Or, if you're feeling generous, 36 non-blank lines. And I even tried to
> scrunch Lambda.toString() onto one line, even though it's really too big.
Now try to write an object system in haskell.
> Now suppose that, for some strange reason, I want to be able to check
> whether two expressions are the same. (Realistically, I can't think why
> you'd want to do that for anything except variables.) In Haskell, I can
> simply append "deriving Eq", and the compiler will write the code for me
> in the obvious way. In this instance, it does the right thing. In
> Java... well OK, I'll go easy on Java and only write the code for
> Variable. Here goes:
>
> public class Variable extends Expression
> {
> private String Name;
>
> public Variable(String n) {this.Name = n;}
>
> public String toString() {return this.Name;}
>
> public boolean equals(Object obj)
> {
> if (obj == null) return false;
>
> if (obj instanceof Variable)
> {
> Variable v = (Variable) obj;
> return this.Name.equals(v.Name);
> }
>
> return false;
> }
>
> public int hashCode() {return this.Name.hashCode() + 9;}
> }
>
> OK, so only 14 lines of actually new code. The hashCode() override is
> not /strictly/ necessary, but the IDE whines like hell if you don't, and
> it's highly likely that you're going to put variables as keys into a
> HashMap at some point, in which case hashCode() had better work correctly!
>
> In Haskell, you can simply say
>
> e `equals` f =
> case (e, f) of
> (Variable x, Variable y) -> x == y
as far as I can tell, this is not the same code as the java one: you're
not checking here if the variables have the same name too, only if they
are instances of the same type.
>
> and you're done.
>
> In Java, you have to check whether the argument is null. (Impossible in
> Haskell.)
it's about the same as checking for an empty list.
BTW, that check could be at the base class in an ancestor method. But
you wrote it as abstract for whatever reason...
> You have to check whether the argument is of a comparable
> class. (Haskell's static type checking does that for you at
> compile-time. Actually Java's generics could do that too if they
> extended it a teeny weeny bit... but they haven't.)
I'm sorry, but if you wanted at least some type checking in java,
shouldn't you do:
public boolean equals(Expression obj)
instead of
public boolean equals(Object obj)
?
The compiler then would warn you whenever you passed something like
exp.equals(2) (which, yes, reads kinda weird since a number literal like
that should be an expression after all). Then again, even as is it
already should warn you, since it expects an instance of Object...
> In consequence to the above, I just spent an entire morning coding in
> Java, and all I actually have to show for it is a boat-load of classes
> which can hold data and generate a graphical representation of
> themselves. They have no actual /behaviour/ yet. It's taken me this long
> just to do that... Man, Java is a blunt knife! (But oh so hard to hurt
> yourself with... yeah, right!)
all in all, it read like object oriented code from a C programmer...
now, I'm not a java programmer nor enjoy it. But people frequently
enjoy making it sound actually worse than it is.
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
>> Suppose, for example, that I wanted to implement a lambda expression
>> interpreter.
>
> I don't think most java programmers would want that. :p
And I care because...? ;-)
> but kudos for installing and configuring netbeans just for that... :)
Well, not /just/ for that. It was the smallest example I could think of
off the top of my head. The /real/ application I'm trying to build would
take way longer to explain.
>> In reality, you /probably/ want to make it insert some brackets and
>> stuff
>
> you really can't spell parenthesis, huh? ;)
I really can't *pronounce* that out loud, no.
Besides, why use 7 syllables when 2 will do? ;-)
>> Now let's try the same thing in Java:
>> If your screen is anything like mine, that lot doesn't even fit on one
>> page. This is /a lot/ of code.
>
> Java is very obviously verbose, and it doesn't help when people even go
> further and write damn "public" all over in every occasion!
It's part of the language syntax. (Amusingly, you're not actually
/allowed/ to declare a class as private...)
> class Apply? Really? The name itself and the behaviour conveying an
> action shouldn't be best served as methods of Expression?
It denotes an expression where a function is applied to an argument.
It's part of the parse tree. It's data. That's what an object is
supposed to be: data.
Now, if this was an airline booking system, and somebody wrote a class
implementing the process of applying for a ticket, then, yuh, that's not
a sensible design. That's an action. Objects aren't supposed to be actions.
> Now try to write an object system in haskell.
That's not even hard. :-P
Functions are first-class, right? So you can create a "class" which is
simply a data structure that contains "methods" as fields with function
types. The result is something like JavaScript's notion of objects.
Or, if you don't want to do it that way, you can use Haskell's existing
type-class system to do it more naturally. Even mutable objects aren't
especially hard. And the module system easily gives you public and
private methods and fields.
Dynamic upcasts and downcasts are a bit more work. You can use
Data.Dynamic to implement that. And Haskell already has "generics" out
of the box. :-P Even multiple inheritance is no real problem. (Or at
least, implementing multiple interfaces. Actually /inheriting/ stuff
without implementing it more than once is mildly harder.)
In short, it's not hard to implement a weak language in a stronger one.
Try implementing a Pascal compiler in 1980s era BASIC. Good luck with
that. Now try implementing a BASIC interpreter in Pascal. Pretty easy,
isn't it?
>> Now suppose that, for some strange reason, I want to be able to check
>> whether two expressions are the same.
>> OK, so only 14 lines of actually new code. The hashCode() override is
>> not /strictly/ necessary, but the IDE whines like hell if you don't, and
>> it's highly likely that you're going to put variables as keys into a
>> HashMap at some point, in which case hashCode() had better work
>> correctly!
>>
>> In Haskell, you can simply say
>>
>> e `equals` f =
>> case (e, f) of
>> (Variable x, Variable y) -> x == y
>
> as far as I can tell, this is not the same code as the java one: you're
> not checking here if the variables have the same name too, only if they
> are instances of the same type.
Wrong.
"e" and "f" are lambda expressions. If they are both variables, then "x"
is the variable name of "e", and "y" is the variable name of "f". The
line "x == y" does what you'd imagine it does - i.e., it checks whether
the names are the same.
>> In Java, you have to check whether the argument is null. (Impossible in
>> Haskell.)
>
> it's about the same as checking for an empty list.
Except that, when you're not dealing with a list, then you never have to
worry about it being empty, do you? :-P
> BTW, that check could be at the base class in an ancestor method. But
> you wrote it as abstract for whatever reason...
I'm not even sure what that's supposed to mean.
>> You have to check whether the argument is of a comparable
>> class. (Haskell's static type checking does that for you at
>> compile-time. Actually Java's generics could do that too if they
>> extended it a teeny weeny bit... but they haven't.)
>
> I'm sorry, but if you wanted at least some type checking in java,
> shouldn't you do:
>
> public boolean equals(Expression obj)
>
> instead of
>
> public boolean equals(Object obj)
>
> ?
Unfortunately, Object.equals() is defined to take Object as a parameter,
not any more specific subclass. So, no, you can't do that.
(Or rather, you can, but now you're not overriding Object.equals(),
you're just creating a new, unrelated method of your own.)
> The compiler then would warn you whenever you passed something like
> exp.equals(2) (which, yes, reads kinda weird since a number literal like
> that should be an expression after all).
Have your forgotten? This is Java. 2 is not an object. Therefore it can
never be passed to something expecting Object.
>> Man, Java is a blunt knife! (But oh so hard to hurt
>> yourself with... yeah, right!)
>
> all in all, it read like object oriented code from a C programmer...
...which is ironic, given how hopeless I am at C. :-P
Seriously, I know how to write object-oriented code. I've been doing it
for years. I have certificates to prove it. I'm just saying, Java makes
everything /so/ much work, it takes forever to actually get anything done.
> now, I'm not a java programmer nor enjoy it. But people frequently enjoy
> making it sound actually worse than it is.
Right now I'm only talking about real, actual problems that I'm running
into right now as I try to write my code.
I almost feel like I should be writing a "compiler" that takes the
3-line version saying what data structure I want, and generates the
5-file 44-line monstrosity that the Java compiler expects. It would save
me a bunch of typing, and then I could go in and write the actual code
that turns it into more than just a dumb data structure...
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 4/10/2012 7:33, Invisible wrote:
> Have your forgotten? This is Java. 2 is not an object. Therefore it can
> never be passed to something expecting Object.
At least they figured out auto-boxing 95% of the way.
> I almost feel like I should be writing a "compiler" that takes the 3-line
> version saying what data structure I want, and generates the 5-file 44-line
> monstrosity that the Java compiler expects. It would save me a bunch of
> typing, and then I could go in and write the actual code that turns it into
> more than just a dumb data structure...
Ah, good old code generation. Hope you don't have to track down a bug in the
generated code anywhere.
--
Darren New, San Diego CA, USA (PST)
"Oh no! We're out of code juice!"
"Don't panic. There's beans and filters
in the cabinet."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
>> I almost feel like I should be writing a "compiler" that takes the 3-line
>> version saying what data structure I want, and generates the 5-file
>> 44-line
>> monstrosity that the Java compiler expects. It would save me a bunch of
>> typing, and then I could go in and write the actual code that turns it
>> into
>> more than just a dumb data structure...
>
> Ah, good old code generation. Hope you don't have to track down a bug in
> the generated code anywhere.
Well, there's code generation, and there's code generation.
It depends whether you're using a generator to quickly build a skeleton
for you to fill in, or whether you're using a generator so that you can
code in the source language and not the destination one. ;-)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 10/04/2012 03:33 PM, Invisible wrote:
> Objects aren't supposed to be actions.
...and then I saw this:
http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html
As an aside, if anybody thinks *Haskell* is hard, try frigging
understanding the Java APIs! That stuff is just *insane*... >_<
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
>> but kudos for installing and configuring netbeans just for that... :)
>
> Well, not /just/ for that. It was the smallest example I could think of
> off the top of my head. The /real/ application I'm trying to build would
> take way longer to explain.
The real application, obviously, was Logic Box. ;-)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
|
|