"constraints placed on the nodes to enforce rules about which nodes can connect to which nodes" I couldn't agree more but I guess I don't see how to express certain constraints in this particular framework.

say you have a function F : (forall a. a -> a) -> Int (where ':' means "of type") and a function G : (Int -> Int) -> Int. As well say there are two expressions, X : forall a. a -> a and Y : Int -> Int

Assume two trees (call them A and B) have forms (F X) and (G Y) respectively and A and B are selected for crossover. The nodes for X and Y are chosen to be swapped. sense X's type unifies with Int -> Int and Y's type unifies with forall a. a -> a the crossover is allowed. That said it should not be allowed that Y be passed to F. The expression (F Y) is ill-typed. On the other hand the expression (G X) is fine and perfectly valid.

How then can I express the constraint that Y can not be passed to F but that X can be passed to G? Sense compatibleWith is commutative and the constraint I specify is not it would appear that this cannot be expressed using compatibleWith; something else must be needed.

I like functional programming and type systems, hence the choice of types. I can reformulate this in terms of Java or some other language however. For instance say there is an interface class "Shape" and another class "Square". say F : Square -> Int, G : Shape -> Int, X has type Square, and Y has type Shape. The same issue arises F Y is ill-typed but G X is not.

The issue seems to arise anytime a return type is allowed to be more than atomic, that is there is a notion of more than 1 type "belonging" to the return type weather it be parametric polymorphism, set types, or subclass polymorphism.

Am I missing something and/or is there a way to express the desired constraint?

On Jan 11, 2014 10:21 AM, "Sean Luke" <[log in to unmask]> wrote:
Types are not data passed between nodes.  They are merely constraints placed on the nodes to enforce rules about which nodes can connect to which nodes.

Now, it's true that types often are correlated with the kinds of data that nodes send to each other, but that's hardly a requirement.  For example, we're using types right now simply to force certain kinds of nodes to appear in the top area of a tree but not down near the leaf nodes.

The most common situation you'll find is argument types which are set types, and return types which are atomic types.  For example, you might have a node which returns int plugged into an argument which can accept int or double.  You've seen that before in real programming languages I'm sure: the + operator in most programming languages can take floats, ints, doubles, longs, bytes, indeed in some cases strings.

All you have to do is handle multiple kinds of data in your GPData object.  The typing mechanism merely guarantees that when a node receives a GPData object, what you receive has been constrained in a way that you had specified.


On Jan 11, 2014, at 10:52 AM, Jake Ehrlich wrote:

> OK that explains more. Thanks for the clarification
> As to the example, yes you understood. I am still confused however on how something of type {s, i} can be passed in to something expecting an integer. What if that something returned a string?
> On Jan 11, 2014 8:59 AM, "Sean Luke" <[log in to unmask]> wrote:
> On Jan 11, 2014, at 2:00 AM, Jake Ehrlich wrote:
> > In the documentation for ec.gp.koza.CrossoverPipeline it says "Then a random
> > node is chosen in each tree such that the two nodes have the same return
> > type". Does "same return type" in that quote mean that the types "fit" by
> > GType's compatibleWith method?
> The documentation is misleading.  The correct form is:
> Two nodes M and N are chosen.  Let R(N) and R(M) be the return types of
> M and N respective.  Let A(N) and A(M) be the types of the argument slots
> which N and M respectively fill in their parents.  Then M and N are only
> valid if R(N) is type compatible with A(M) and R(M) is type compatible
> with A(N).
> > First off as I understand it GType.compatibleWith is assumed to be
> > commutative (that is t1.compatibleWith(init, t2) is true if and only if
> > t2.compatibleWith(init, t1) is true). Is this correct or is it just one way?
> compatibleWith is commutative.
> > Say you have a node X of type {string, int} (a set type) and another node Y of
> > type int (an atomic type). X and Y are being passed in as parameters of type
> > {string, int} and int respeticvlly. These return types are "compatible" ("fit") by
> > the definition of compatibleWith for GPAtomicType and GPSetType yet you
> > shouldn't be allowed to swap them sense the parameter type of X's parent
> > can't handle a string, only an int.
> Let me make sure I understand what you're saying.  Node X is of return type {s,i} and it's attached to an argument slot of type {s,i}.  And node Y is of return type i and is attached to an argument slot of type i. If you swapped them, then node X would now be in the argument slot i (which it is type-compatible with) and node Y would now be in argument slot {s,i} (which it is also type-compatible with) so everything would be fine.
> Sean