On Thu, 2006-10-05 at 19:23 -0700, Erick Tryzelaar wrote:
> skaller wrote:
> That one worked as well! Now for the grand finale, using a typeclass
> without opening the module:
>
>
> #import <flx.flxh>
>
> open List;
>
> typeclass Str[T] {
> virtual fun str2: T->string;
> }
>
> module Foo {
> instance Str[int] {
> fun str2 (x:int):string => str x;
> }
>
> fun foo[T with Str[T]] (x:T):string => str2 x;
>
> fun foo[T with Str[T]] (x:list[T]):string =>
> match x with
> | Empty[T] => "()"
> | Cons(?h, ?t) => "(" + (str2 h) + ", " + (foo t) + ")"
> endmatch
> ;
> }
>
> print$ Foo::foo 5; endl;
> print$ Foo::foo$ list(1,2,3,4); endl;
> CLIENT ERROR
> [lookup_name_in_env]: Name '_inst_Str' not found in environment (depth 2)
> In foo.flx: line 14, cols 18 to 24
> 13:
> 14: fun foo[T with Str[T]] (x:T):string => str2 x;
> *******
> 15:
In fact this should NOT work. The diagnostic is misplaced.
I have fixed it:
[EMAIL PROTECTED]:/work/felix/svn/felix/felix/trunk$ f er4
CLIENT ERROR
[lookup_name_in_env]: Name '_inst_Str' not found in environment (depth
2)
In er4.flx: line 25, cols 8 to 17
24:
25: print$ Foo::foo 5; endl;
**********
26: print$ Foo::foo$ list(1,2,3,4); endl;
See also er4.flx: line 15, cols 18 to 24
14:
15: fun foo[T with Str[T]] (x:T):string => str2 x;
*******
16:
What's happening is we are trying to find an instance with
this Ocaml function:
let luqn2 qn = lookup_qn_in_env2 syms caller_env qn in
(* ********** *)
The 'qn' is the Str on line 15, in foo definition,
hacked to _inst_Str.
But we're looking in the caller environment on line 25,
and indeed there is no instance of Str visible there.
So the diagnostic is correct.
Note that 'in theory' this means you can have two
distinct instances in different scopes. In fact,
it is a limitation of the current implementation
that you cannot have this. The chosen instance is NOT
propagated as it should be by the instantiator.
Instead .. the code generator just looks up the mapping:
Str, int -> instance
that is, it finds the instance by the unique combination
of the typeclass plus type arguments: in other words
it takes that as a partial function from typeclass
type list pairs to instances.
This precludes more than one instance of a typeclass
with the same type arguments.
This works like C++ specialisation: the one definition rule
forbids two specialisations of a template with the same
type arguments, and this allows the specialisations to be
found very late -- by the linker in fact. All hell breaks
loose with dynamic linkage .. :)
An advantage of the restriction is that the semantics
of a call are independent of where the call is made,
which is useful given that typeclasses are a HACK!
The call finds the instance in context implicitly.
Implicit coupling is BAD. This smacks of C++ 2 phase
name lookup (where the second phase finds named in
a template by looking in the scope at the point
of use .. which means two instances might lead to
*different* bindings).
If the typeclass instance had to be passed *explicitly*
this wouldn't be a problem, eg:
foo[int with Str[int]] (1)
would be passing 'Str[int]' instance named in the CALL
to the function as an argument .. just like the '1'.
There is a good argument typeclasses are a bad idea
and you should actually pass a *real* class instance
instead .. which is several orders of magnitude more
powerful .. since it can also bind dynamic instances
with variables (class members).
In fact the funny thing is you can ALREADY do this
in both Felix AND in C++. In fact Felix can't do it
with polymorphic methods, but C++ CAN do so,
and in fact this is really USED in C++ allocators.
So actually .. C++ is way ahead of Haskell.
Anyhow to get back to the error, here's another way to
look at it: you don't expect this to work, do you? :
module X { fun f(x:int)=> x; }
print$ f 1;
Why not? Because f isn't visible. So let me rewrite your example:
///////////////////////////////////
#import <flx.flxh>
open List;
typeclass Str[T] {
virtual fun str2: T->string;
}
module Foo {
instance Str[int] {
fun str2 (x:int):string => str x;
}
}
fun foo[T with Str[T]] (x:T):string => str2 x;
fun foo[T with Str[T]] (x:list[T]):string =>
match x with
| Empty[T] => "()"
| Cons(?h, ?t) => "(" + (str2 h) + ", " + (foo t) + ")"
endmatch
;
print$ foo 5; endl;
print$ foo$ list(1,2,3,4); endl;
///////////////////////////////
I lifted the foo out of module Foo, because it isn't
relevant that they're defined in there.
When these 'foo' are bound, the 'Str[t]' in them
binds against the *typeclass* Str NOT against instances.
The 'str2' in the definition binds against a typeclass
specialisation -- which is a kind of synthesised instance:
fun foo[T with Str[T]] (x:T):string => str2 x;
really reads:
fun foo[T with Str[T]] (x:T):string {
open Str[T];
return str2 x;
}
That's processing the *definition* of foo.
Processing the *call* of foo, on the other hand,
requires finding and 'passing logically' an instance
corresponding to the typeclass specialisation Str[int],
which is named _inst_Str. Of all the visible _inst_Str,
the one for which the type variable matching the T
in the typeclass is instantiated to 'int' is chosen.
But in the rewritten (and original) example there are
NO visible instances: the only instance is hiding in module Foo.
C++ agrees with this rule. You may not call a template which
has a partial specialisation unless the partial specialisation
is declared in the scope in which the call is made -- it can
be defined elsewhere but it MUST be visible in the caller's
scope (note: it doesn't have to be IN the callers scope,
in fact it isn't allowed to be, unless that scope also happens
to be the scope in which the master template is defined:
the specialisation must be defined in the same namespace
as the template being specialised.)
This is not required in Felix and can't easily be,
since modules aren't namespaces .. they can't be
extended.
For a given type such as 'string' or whatever, any
instances for it should probably be put in the same
module as the type is defined. EG:
module String {
type string = "string";
instance Categories::TotalOrder[string]
{
fun compare: string * string -> int = "compare($1,$2)";
}
}
The instances will be made available if, and only if,
you open the module. It's all very unfortunate: there's
no way to explicitly name an instance, like:
print$ Foo::foo[int with Foo::Str[int]] 5; endl;
Perhaps there should be.
--
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Felix-language mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/felix-language