I have a file with a generic function fv in it:
module SanityCheck
import Set;
import String;
public set[str] fv(&Heap G) = {fv(ei) | ei <- range(G)} - domain(G);
that I use in a separate file:
module Test
import Map;
import Set;
import String;
import Expression;
import SanityCheck;
import SimplifiedHeap;
public Heap GG() {
return (...);
}
public set[str] gfv() = fv(GG());
Now, the following Console session then doesn't make any sense to me. This I say because here -- as imported from SimplifiedHeap -- the type Heap is a map, and, given that I have already imported Map, Rascal has whatever it takes to infer that the Map instance is the intended one.
rascal>import Test;
ok
rascal>gfv();
project://Launchbury2/src/SanityCheck.rsc:16,46: Undeclared variable, function or constructor: range
It seems to me that Console wants the instance of range used in Test.rsc to be inferable in SanityCheck.rsc!!! Is this really what Rascal demands? My question then is how do you guys ever manage Generic Programming?
More specifically, I have two different Heap implementations with the same name and interface. I have a set of (heap) sanity checks that I perform before evaluation using either implementation. The code reuse idea behind the above (currently problematic) design was then to reuse the same sanity check framework regardless of the Heap implementation chosen. In case you think the above design is not how you would achieve that goal in Rascal, please instruct me on how to revise it. For example, do you accomplish similar code reuse tasks via runtime polymorphism only? If so, what's the way to do so?
This really isn't a problem with polymorphism, it's an error with resolving the function named range. A module only sees local definitions and public definitions in imported modules. Here, range is in Map, but SanityCheck doesn't import Map. Importing two modules into a third does not make the two able to "see" one another. Once you import Map into SanityCheck, this code should work.
However, it still isn't statically correct, although Rascal may let it through for now (since we check these things mostly dynamically at the moment, hence the error when you try to run gfv()). Your type variable, &Heap, is unbounded, meaning it has the bound value. However, you don't have a function called range that takes a value of type value, so there is no guarantee that this will resolve to a specific function at runtime.
The best way to handle this may be to define a data type like:
data Heap[&D,&R];
then, in a module that gives your implementation of heaps, you could define a constructor holding your representation:
data Heap[&D,&R] = simpleHeap(map[&D,&R]);
and the functions you need:
public &R heapRange(simpleHeap(map[&D,&R] m)) = range(m);
This should work -- you would just import different modules for different implementations. We don't really have a way to define an interface of what these implementations should implement, except through default functions, so your definition of Heap could also include something like:
public default &R heapRange(Heap[&D,&R] _) { throw "Not implemented"; }
This would at least give an idea of what functions you are expected to support, and would provide default behavior (with the bonus of making the future static type checker happy).
I'm typing all this in directly, but I'll check these examples later and make sure they are actually correct Rascal. This should at least give you an idea of what you need to do, though.
Asked: Jan 11
Seen: 14 times
Last updated: Jan 12
Copyright CWI, 2010-2012. Content on this site is licensed under a Creative Commons Attribution Share Alike 3.0 license.