Closed callendorph closed 1 year ago
Apologies for missing this issue.
The cannot access field xyz
error message are indeed confusing. I'll dig in and figure out what is happening.
Hi Carl,
I took a look through the code, and found the root cause. First, let's dissect those confusing error messages a bit. We need to improve these messages, but here is where they came from:
lostanza defmethod write-buf<Double> (C:ref<Tuple<Double>>, P:ref<GPolynomial<Double>>) -> ref<Int> :
for (var i:int = 0, i < P.size, i = i + 1) :
val x = C.items[i]
P.raw[i] = x.value
return new Int{0}
In the function above, the write-buf
method actually takes one type argument called Double
. So in the body of the function, Double
actually refers to the type variable, and not the definition in core
.
This is why you get the message:
Cannot access field value in expression of type ref<Double>.
Because Double
is just an opaque type variable, and has no fields at all.
Other than the error message, the other issue is the use of the parametric types for this purpose. Stanza does not allow you to perform dispatch upon the "parametric" properties of any type.
E.g. Stanza will let you do a runtime dispatch between Int
or String
. But it won't let you do a runtime dispatch between Tuple<Int>
or Tuple<String>
. This is because in general, especially if users make heavy use of the ?
type, Stanza doesn't know the contents of the collection.
In your specific case, you can use overloaded versions of the functions.
lostanza defn elem-size (C:ref<Tuple<Double>>) -> ref<Int> :
return new Int{sizeof(double) as int}
lostanza defn elem-size (C:ref<Tuple<GComplex>>) -> ref<Int> :
return new Int{2 * sizeof(double) as int}
lostanza defn write-buf (C:ref<Tuple<Double>>, P:ref<GPolynomial<Double>>) -> ref<Int> :
for (var i:int = 0, i < P.size, i = i + 1) :
val x = C.items[i]
P.raw[i] = x.value
return new Int{0}
lostanza defn write-buf (C:ref<Tuple<GComplex>>, P:ref<GPolynomial<GComplex>>) -> ref<Int> :
for (var i:int = 0, i < P.size, i = i + 1) :
val z = C.items[i]
P.raw[2 * i] = z.x ; real(z).value
P.raw[2 * i + 1] = z.y ; imag(z).value
return new Int{0}
The above will just create four different functions, where two pairs happen to share the same name. Stanza will statically choose which one to call based upon the static types.
For your general constructor function you can type it out twice yourself:
public lostanza defn GPolynomial (C:ref<Tuple<Double>>) -> ref<GPolynomial<Double>> :
val N:int = length(C).value
if N <= 0 :
throw(GSLException(gsl-EINVAL()))
val sizeT = elem-size(C)
val buf = call-c clib/malloc( N * sizeT.value ) as ptr<double>
val ret = new GPolynomial<Double>{buf, N, C}
write-buf(C, ret)
return ret
public lostanza defn GPolynomial (C:ref<Tuple<GComplex>>) -> ref<GPolynomial<GComplex>> :
val N:int = length(C).value
if N <= 0 :
throw(GSLException(gsl-EINVAL()))
val sizeT = elem-size(C)
val buf = call-c clib/malloc( N * sizeT.value ) as ptr<double>
val ret = new GPolynomial<GComplex>{buf, N, C}
write-buf(C, ret)
return ret
Or you can also use the #for
macro to do a copy-and-replace for you:
#for TYPE in (Double GComplex) :
public lostanza defn GPolynomial (C:ref<Tuple<TYPE>>) -> ref<GPolynomial<TYPE>> :
val N:int = length(C).value
if N <= 0 :
throw(GSLException(gsl-EINVAL()))
val sizeT = elem-size(C)
val buf = call-c clib/malloc( N * sizeT.value ) as ptr<double>
val ret = new GPolynomial<TYPE>{buf, N, C}
write-buf(C, ret)
return ret
The parametric types are definitely the most difficult aspect of Stanza -- and for C++ programmers especially. Stanza's parametric types work closest to Java's generics. C++'s template system acts more like a sophisticated copy-and-replace system, rather than a typing feature, and so just has very different behaviour.
Ya - so it seems like what I've done is just over complicate things. The key part seems to be to let the overloaded function types do the work. The parametric typing really only comes in to play for the definition of the GPolynomial type.
E.g. Stanza will let you do a runtime dispatch between Int or String. But it won't let you do a runtime dispatch between Tuple
or Tuple . This is because in general, especially if users make heavy use of the ? type, Stanza doesn't know the contents of the collection.
Does this same logic apply for all collection types - like Array or Vector ? Or is it only specific to Tuple ?
Is the issue here that the type capture can only happen at one level ? ie, in some of my examples with the Tuple it is like trying to use the type capture at multiple levels, the Polynomial and the Tuple. So there is no way for the top-level to know what type got captured in the child Tuple type?
#for
OK - ya that's interesting. I'll try that out.
Thank you!
Does this same logic apply for all collection types - like Array or Vector ? Or is it only specific to Tuple ?
It applies to all collection types and parametric types. This includes function types as well: there is no way for Stanza to perform a runtime-dispatch between:
Int -> Int
and
Int -> String
Is the issue here that the type capture can only happen at one level ? ie, in some of my examples with the Tuple it is like trying to use the type capture at multiple levels, the Polynomial and the Tuple. So there is no way for the top-level to know what type got captured in the child Tuple type?
That's pretty much correct. In the Stanza virtual machine all objects are tagged with a small integer that tells it what type of object it is. This tag says something like: "Int", or "Tuple", but it doesn't say anything about the contents. The runtime dispatch is only allowed to look at this tag to do its work.
Hope that makes it clearer! Patrick
for TYPE in (Double GComplex) :
I think the syntax here is wrong. It must be:
#for TYPE in [Double GComplex] :
...
[] for a Tuple, () for a List.
Sorry, yup your syntax is the right one:
#for TYPE in [Double GComplex] :
...
Hi,
I'm trying to write a parametric type for a wrapper around the GSL Polynomial implementation. The GPoly has two types - real valued coefficients and complex coefficients. I'm trying to write a Parametric typed implementation. I think I'm stumbling on both a lack of understanding of the parametric typing and an error message that is particularly difficult to understand when debugging.
I first tried doing something like this:
But this snags on the
match
- it seems like it can't operate in the templated regime becauseref<T>
never matches on anything concrete.My guess is that this is why the
sizeof
is failing as well. I see places incore.stanza
wheresizeof
is used ondeftype
objects - specifically inallocate-initial-stack
onStack
. So I think it must be something to do with theref<T>
again.I ran into a similar roadblock with
ref<T>
when I tried to make functions such as:It throws errors like this:
OK - so I'll try something different:
This snags on a very strange error message:
It is erroring out on the line
buf[i] = x.value
. That is very strange to me because the.value
seems to be the way to accessref<Double>
in every other part of the code.So - this must not be the way to write functions for parametric types - but it isn't clear why. The error message is not particularly helpful.
So finally - I back track again and implement this:
Which works - it compiles and I can evaluate the generated real-value coeff polynomial with it.
I think this means I will also have to write a
GComplexPolynomial
that does something very similar except for complex value coeffs and returnsref<GPolynomial<GComplex>>
.OK - but I don't want to have duplicate code in
GRealPolynomial
andGComplexPolynomial
- so I'll take one last stab at this. It seems like I would have to write some kind ofdefmulti write-buf
for both theTuple<Double>
andTuple<GComplex>
. If this existed I could replace the for loop portion with a call to that function and then be able to use captured types.So I make the following attempt:
This feels like it is really close - but the compiler still barfs with:
What is strange about this result is that It doesn't like it when I access the ref types in the
C.items
list, but it doesn't seem to mind that I accessP.raw
andP.size
directly.I tried removing the
for
loop and just access the first element of the tuple. That still throws the same error - so I don't think this is related to the for loop. Is this a restriction on theTuple
elements ? I'm not trying to write them - just read them.I've pushed this last implementation here:
https://github.com/callendorph/lbstanza-gsl/tree/issues/new_polynomial
Thoughts ? Am I doing this wrong ? Do any of these feel like the right direction ?
Does the
Cannot access field * in expression of ...
error make sense to you ?