structural typing?

174 views
Skip to first unread message

Raoul Duke

unread,
Apr 21, 2012, 8:14:04 PM4/21/12
to haxe...@googlegroups.com
anybody have docs / tutorial / example code of using structural types
in haxe? googling doesn't turn up much that i can see?

Raoul Duke

unread,
Apr 21, 2012, 8:15:27 PM4/21/12
to haxe...@googlegroups.com
for example, i seem to have to manually "cast" my class instance into
the structural type that the method parameter is defined to have.

David Holaň

unread,
Apr 21, 2012, 9:00:33 PM4/21/12
to haxe...@googlegroups.com
How did you define your structural type? Check syntax used in Iterator<T>, that works for me.

2012/4/22 Raoul Duke <rao...@gmail.com>

Raoul Duke

unread,
Apr 21, 2012, 9:01:34 PM4/21/12
to haxe...@googlegroups.com
On Sat, Apr 21, 2012 at 6:00 PM, David Holaň <paladin....@gmail.com> wrote:
> How did you define your structural type? Check syntax used in Iterator<T>,
> that works for me.

will do!

Raoul Duke

unread,
Apr 21, 2012, 9:09:19 PM4/21/12
to haxe...@googlegroups.com
it was probably the return type.

the copy-source doesn't care what the return type of the set-fn is,
since it is just setting. so it could consider it as void.

but those things which implement the set-fn, those i wanted to return
their own type so i could chain calls (i'm a train wreck engineer i
guess).

so in the end just now i tried the typedef with Dynamic return type,
that let me not have to cast, it seems, but i dunno what the full type
and performance implications are :-)


public function copyTo( ?vout: { setXY:Float->Float->Void } ):Void {
vout.setXY( x, y );
}

vs.

typedef SetXY = { function setXY(x:Float, y:Float):Dynamic; }

public function copyTo( vout:SetXY ):Void {
vout.setXY( x, y );
}

Nicolas Cannasse

unread,
Apr 21, 2012, 10:58:59 PM4/21/12
to haxe...@googlegroups.com
Le 22/04/2012 02:14, Raoul Duke a �crit :

> anybody have docs / tutorial / example code of using structural types
> in haxe? googling doesn't turn up much that i can see?

Only structures and classes -> structures use structural subtyping, you
cannot have structural subtyping between two classes for instance
(that's classic inheritance then).

Check http://haxe.org/manual/struct

Best,
Nicolas

Raoul Duke

unread,
Apr 22, 2012, 1:26:32 AM4/22/12
to haxe...@googlegroups.com
On Sat, Apr 21, 2012 at 7:58 PM, Nicolas Cannasse
<ncan...@motion-twin.com> wrote:
> Only structures and classes -> structures use structural subtyping, you
> cannot have structural subtyping between two classes for instance (that's
> classic inheritance then).
> Check http://haxe.org/manual/struct

thanks! for some reason when i read the struct docs before it didn't
make sense, but i think i get it now.

Plop

unread,
Aug 2, 2012, 9:35:27 AM8/2/12
to haxe...@googlegroups.com
hi,

is it planned in the future of haxe 3/4/X, having full structural typing ?  interoperability between all kind of data structure ?
named structures ( haxe classes/interfaces...) anonymous structures ( haxe structures)
What could actually prevent this feature ? ( syntax level, and compiler implementation )

i'm really interested about haxe internal and future and some part of the compiler are still obscure to me.

thanks !

Nicolas Cannasse

unread,
Aug 2, 2012, 4:27:09 PM8/2/12
to haxe...@googlegroups.com
Le 02/08/2012 15:35, Plop a �crit :
It's already in Haxe, since the beginning.
Check http://haxe.org/manual/struct#structural-subtyping

Best,
Nicolas

Plop

unread,
Aug 3, 2012, 6:26:53 PM8/3/12
to haxe...@googlegroups.com
Hi Nicolas, list,

yes indeed it's already great enough ! but I mean :


typedef SPoint =
{
  var x : Int;
  var y : Int;
  function add( p : Point ) : Void;
}


class CPoint
{
   
public var x : Int;
   
public var y : Int;
   
   
public function new(x,y)
   
{ this.x = x;
     
this.y = y;
   
}

   
public function add( p : Point ): Void {...}
}


var cpt: CPoint = new CPoint(0,3);

var spt: SPoint = { x : 0, y : 5, add : cpt.add }; // no error. good
spt
.add = cpt.add; // forbidden since spt.add is not marked as dynamic . good

var cpt2: CPoint =  spt;
//ERROR { y : Int, x : Int, add : p : Point -> Void } should be CPoint
// why ? cpt2 is a CPoint so modifying add field is forbidden. ( and spt.add is not marked dynamic)
// I can't try (will not be allowed) to modify the function reference. The structures are compatibles.

// finally at syntax level, no differences between
cpt
.add({x:3, y:4});
spt
.add({x:3, y:4});


typedef SDPoint =
{
 
var x : Int;
 
var y : Int;
 
dynamic function add( p : Point ) : Void; // ==   var add:Point->Void;
}


var sdpt: SDPoint;
sdpt
= { x : 0, y : 5, add : cpt.add };   //no error . good.
spt
=  { x : 0, y : 5, add : cpt.add };   //no error . good
spt
= cpt;   // no error . good.
sdpt = spt;  // error expected . good. Field add is method but should be dynamic method.
sdpt = cpt;  // error expected . good. Field add is method but should be dynamic method.

cpt = sdpt;  //error is { y : Int, x : Int, add : p : Point -> Void } should be CPoint.
// the expected error should be 'Field add is dynamic but should be method.'

cpt
= spt;  //error is { y : Int, x : Int, add : p : Point -> Void } should be CPoint.
// ?? struct and access levels are compatibles. This expression should be allowed


I understand why actually we can't do some variances. It's really for  goods reasons (for example you don't want member method to be replaced at runtime...) and you want also to statically control, at compile time some level of variance, visibility and Access control.  I agree to offers that to the designer!

Also having the choice to switch between struct and classes in both way would be really great. My questions were about that. 

I like the idea having class like a top level abstraction, an alias of a structured type. I like also the implements class/interface keyword and extends too because since there are not needed for a fully structural typing system they guide the developer to provide interoperability between it's data structures.

briefly, indeed, I love the philosophy behind haxe ! great job !


Cheers !

Raoul Duke

unread,
Aug 3, 2012, 6:43:50 PM8/3/12
to haxe...@googlegroups.com
> Also having the choice to switch between struct and classes in both way
> would be really great. My questions were about that.

+ a lot, i am just right this second frustrated with that :-) i always
forget i can't do it. :-(

Raoul Duke

unread,
Aug 3, 2012, 6:44:43 PM8/3/12
to haxe...@googlegroups.com
On Fri, Aug 3, 2012 at 3:43 PM, Raoul Duke <rao...@gmail.com> wrote:
> + a lot, i am just right this second frustrated with that :-) i always
> forget i can't do it. :-(

or i am confused.

http://haxe.org/manual/2_types

claims i can pass an object instance into something that accepts
structural types...

Raoul Duke

unread,
Aug 3, 2012, 6:46:56 PM8/3/12
to haxe...@googlegroups.com
On Fri, Aug 3, 2012 at 3:44 PM, Raoul Duke <rao...@gmail.com> wrote:
> claims i can pass an object instance into something that accepts
> structural types...

or, is it because i am trying to do things in a collection (array)?

LevelUtilTest.hx:15: characters 49-54 :
Array<com.example.mzzl.client.test.FakeLevelSpec> should be
Array<com.example.mzzl.client._LevelUtil.LevelTypeSpec>
LevelUtilTest.hx:15: characters 49-54 : Type parameters are invariant
LevelUtilTest.hx:15: characters 49-54 :
com.example.mzzl.client.test.FakeLevelSpec should be
com.example.mzzl.client._LevelUtil.LevelTypeSpec
LevelUtilTest.hx:15: characters 49-54 :
com.example.mzzl.client.test.FakeLevelSpec should be { type :
com.example.mzzl.client.LevelType }
LevelUtilTest.hx:15: characters 49-54 : For function argument 'levelSpecs'

Nicolas Cannasse

unread,
Aug 4, 2012, 4:03:05 AM8/4/12
to haxe...@googlegroups.com
> *Also having the choice to switch between struct and classes in both way
> would be really great. My questions were about that. *

A class is a specific implementation. A struct is an abstract
definition. Going from a class to a struct makes sense, the other way
doesn't (think for instance if the class has an inline method).

Best,
Nicolas

Plop

unread,
Aug 4, 2012, 10:50:31 AM8/4/12
to haxe...@googlegroups.com
I understand your meaning about the compile time object. But for the runtime object, in memory, not the abstraction, the 2 structure are pretty close. The end user handle each construct the same.
Since all our class' methods aren't really static/read-only (we can override by default thanks to inherence), class are in fact kind of syntaxic sugar for structure with function references. (like C++ virtual method are syntaxic sugar for struct with const function pointer).
Haxe/AS3 class "are" struct with default method body while interface are abstract as structs. 

>A struct is an abstract definition.
But with interface, this don't work too.

typedef SPoint =
{
  var x : Int;
  var y : Int;
  function add( p : Point ) : Void;
}

interface ICPoint
{
 
var x:Int;
 
var y:Int;
 
function add( p : Point ): Void;
}

var  spt: SPoint;
var icpt: ICPoint;
...

spt
= icpt; // no error expected . good !
icpt
= spt; // error { y : Int, x : Int, add : p : Point -> Void } should be ICPoint
//  icpt, spt each are abstract like you said. but this variance is not allowed.




I understand also the dynamic keyword on function def. By default, at runtime we canot change function references for Classes and this level of control is really good. We can only change function reference at compile time for classes thanks to inherence/override.
In fact Haxe/AS3 classes are structurally dynamic and look like struct with const restriction on function references.

So the runtime objects (instance typed ICPoint, SPoint or Cpoint) seem compatibles. (from the syntax level point of view, from the end user perspective)
(I know in compiler internals and target translation it's not so simple :-/ ) 



Benjamin Dubois

unread,
Aug 5, 2012, 1:59:27 AM8/5/12
to haxe...@googlegroups.com
Why I have the feeling this is going to end with a
"You can do it with a macro".

Ben

Plop

unread,
Aug 5, 2012, 2:58:04 PM8/5/12
to haxe...@googlegroups.com
@ Raoul    if you provide some code maybe we could help you. the stack trace isn't enougth :)  !
@ Ben : macro can't do anything , except converting everithing into structs... it's more about the type system and how targets handle the classes and interfaces. (runtime). Sadly, I think It's quite hard to change something like this isn't it nicolas ? :(


Juraj Kirchheim

unread,
Aug 5, 2012, 3:14:47 PM8/5/12
to haxe...@googlegroups.com
On Sat, Aug 4, 2012 at 4:50 PM, Plop <ploplo...@gmail.com> wrote:
Haxe has structural subtyping and nominal subtyping and cleanly keeps
them apart. Here's why:

A pitfall of structural typing versus nominative typing is that
two separately defined types intended for different purposes, but
accidentally holding the same properties (e.g. both composed of a pair
of integers), could be considered the same type by the type system,
simply because they happen to have identical structure. One way this
can be avoided is by creating one algebraic data type for each use. -
http://en.wikipedia.org/wiki/Structural_type_system

If I say "of type ICPoint", I mean an instance, the author of which
consciously decided that they intend it to act as an ICPoint, thereby
fulfilling the contract that goes with it. If I say "of type SPoint",
I merely mean an object, that happens to have the right structure.

AFAIK Google Go does actually use implicit implementation of
interfaces, but I think that's a poor choice. When I write a method,
that consumes an instance of an interface, I want to know that whoever
calls it, really intended for the object at hand to act as such an
interface.

One of Haxe's greatest qualities is, that it doesn't force anything
onto you. If you want the explicitness of nominal subtyping, you can
use it, if you prefer the implicitness of structural subtyping, you
can use that (although it might be nice to have this without the
performance cost on static platforms - IIRC Hugh had some ideas on how
to achieve this with for haxe/cpp).

Regards,
Juraj

Juraj Kirchheim

unread,
Aug 5, 2012, 3:23:13 PM8/5/12
to haxe...@googlegroups.com
On Sun, Aug 5, 2012 at 8:58 PM, Plop <ploplo...@gmail.com> wrote:
> @ Ben : macro can't do anything , except converting everithing into
> structs... it's more about the type system and how targets handle the
> classes and interfaces. (runtime). Sadly, I think It's quite hard to change
> something like this isn't it nicolas ? :(

That's incorrect. Macros can do quite a bit. You can use macros to
build typedef to anonymous types with the same members as a given
interface and vice-versa.
You can also use macros to create adapters just in time, that will box
an anonymous object in an interface.
This would work for example:

Box.asInterface(cast(spt, ICPoint));//wrapping this into a cast,
so that the second argument can be a type.

You can use `haxe.macro.Context.defineType` to define a class that
implements ICType and forwards all calls/properties to the underlying
anonymous object. Not super-pretty or anything, but it works (in fact
in this very case, it doesn't, because x and y are defined as plain
fields, but I would argue, that with interfaces, this is bad practice
to start with).

Regards,
Juraj
Reply all
Reply to author
Forward
0 new messages