Until this chapter, we wrote Core Perl programs without worrying about typing, as Perl has a dynamic typing system. .NET is a strong-typed environment: There is one-to-one correspondence between variables and types. In other words, every variable has one and only one type associated with it.
We do not have to change our attitude with respect to types if our PerlNET program contains Core Perl statements only. However, the problem may occur when working with .NET components and classes.2 As we saw in our first sample program, working with .NET classes involves calling methods.3 Many of the methods expect arguments, and we have to supply arguments of the correct type. Consider the following code example from the Marshalling\Types folder.
# # Types.pl # use strict; use namespace "System"; use PerlNET qw(AUTOCALL); my $x = "0"; print Math->Cos($x);
This program introduces the static method Cos of the Math class located in the System namespace. This class provides constants and static methods for trigonometric, logarithmic, and other common mathematical functions.
We expect our program to print the numeral 1, because we rely on Perl for converting the $x variable from string scalar into floating-point number scalar, as we use $x in the numeric context. Unfortunately, building and running the program ruins our expectations, and instead of printing 1 in output, the program exits with the following error message:
System.ApplicationException: Can't locate public static method Cos(System.String) for System.Math.
The error means that we supplied an argument with an incorrect type to the Cos function, which expects a floating-point number. Perl context-based conversion did not work here because PerlNET runtime modules that serve as a bridge between Perl and .NET environments convert scalars into specific .NET types. PerlNET defines different rules than Core Perl defines for type conversions when we call .NET methods and pass to them Perl scalars.
Internally, Perl caches multiple representations of a value for each varia_ble:
- Numeric (float)
In other words, Perl "remembers" the last representation of a value and sometimes it interprets the type not in the way we want it to.
Back to our example. We assigned 0 to our $x variable. The type of value is String, and this is how it is remembered by $x. To fix it, we should clear the part of $x's memory that remembers the type as String. There are several ways to do it. We may perform mathematical operations on $x that will not change it (e.g., add zero or multiply by 1) and store the result back to $x (the first way in the example below) or pass the mathematical expression to the method without assigning to the variable (the second way shown). Optionally, we may use the Convert class from the System namespace. Its ToDouble function will return a numeric value (the third way). Here are some possible ways to rewrite the Cos statement in our Types.pl program.
$x = $x + 0; print Math->Cos($x); # 1-st way . . . print Math->Cos($x*1); # 2-nd way . . . print Math->Cos(Convert->ToDouble($x)); # 3-rd way
After you pick up the way you like it, rebuild and run the program. You should get a clean execution. We saved the correct version of the program in Marshalling\TypesFix.
However, such solutions will work only for float (double) numbers. To avoid any ambiguity in interpreting your Perl scalar variables, PerlNET offers a set of special casting functions for value types. We explain these functions and marshalling types in Chapter 10 when we discuss calling methods.