In a weakly-typed language, type-casting exists to remove ambiguity in typed operations, when otherwise the compiler/interpreter would use order or other rules to make an assumption of which operation to use.
Normally I would say PHP follows this pattern, but of the cases I've checked, PHP has behaved counter-intuitively in each.
Here are those cases, using JavaScript as a comparison language.
String Concatentation
Obviously this is not a problem in PHP because there are separate string concatenation (.
) and addition (+
) operators.
JavaScript
var a = 5;
var b = "10"
var incorrect = a + b; // "510"
var correct = a + Number(b); // 15
String Comparison
Often in computer systems "5" is greater than "10" because it doesn't interpret it as a number. Not so in PHP, which, even if both are strings, realizes they are numbers and removes the need for a cast):
JavaScript
console.log("5" > "10" ? "true" : "false"); // true
PHP
echo "5" > "10" ? "true" : "false"; // false!
Function signature typing
PHP implements a bare-bones type-checking on function signatures, but unfortunately it's so flawed it's probably rarely usable.
I thought I might be doing something wrong, but a comment on the docs confirms that built-in types other than array cannot be used in PHP function signatures - though the error message is misleading.
PHP
function testprint(string $a) {
echo $a;
}
$test = 5;
testprint((string)5); // "Catchable fatal error: Argument 1 passed to testprint()
// must be an instance of string, string given" WTF?
And unlike any other language I know, even if you use a type it understands, null can no longer be passed to that argument (must be an instance of array, null given
). How stupid.
Boolean interpretation
[Edit]: This one is new. I thought of another case, and again the logic is reversed from JavaScript.
JavaScript
console.log("0" ? "true" : "false"); // True, as expected. Non-empty string.
PHP
echo "0" ? "true" : "false"; // False! This one probably causes a lot of bugs.
So in conclusion, the only useful case I can think of is... (drumroll)
Type truncation
In other words, when you have a value of one type (say string) and you want to interpret it as another type (int) and you want to force it to become one of the valid set of values in that type:
$val = "test";
$val2 = "10";
$intval = (int)$val; // 0
$intval2 = (int)$val2; // 10
$boolval = (bool)$intval // false
$boolval2 = (bool)$intval2 // true
$props = (array)$myobject // associative array of $myobject's properties
I can't see what upcasting (to a type that encompasses more values) would really ever gain you.
So while I disagree with your proposed use of typing (you essentially are proposing static typing, but with the ambiguity that only if it was force-cast into a type would it throw an error — which would cause confusion), I think it's a good question, because apparently casting has very little purpose in PHP.
Queues don't functionally compose, and are difficult to implement in a well-performing way without making them mutable.
The very nature of a queue suggests that you put things into it and take things out of it, which is at odds with the immutable nature of functional languages. Oh, sure, you can do the same thing with lists, but generally what you are really doing is composing a new list, not adding to one, and a list doesn't have the special requirement of forcing you to put items into one end and take them out of the other, like a queue does.
All that said, have a look at Okasaki's paper on Purely Functional Data Structures, which does outline a strategy for creating a queue with adequate performance in a functional way.
Best Answer
Type declarations and static analysis
Since no value typed as
NIL
can exist, the use of theNIL
type is mostly relevant for static analysis.T
andnil
respectively represent the top (⊤) and bottom (⊥) types and might be used when inferring the type of expressions. Consider the following example:(read)
isT
, because if reading succeeds, the returned value can be anything.(1+ (read))
is necessarilyNUMBER
, because the returned value exists only if no error is signaled. And if this is the case, we know from the type analysis of+
that the result is a number.(length (1+ (read)))
isNIL
, becauselength
returns a value only for sequences. Giving a number is guaranteed to signal an error, and thus the possible values returned by the function is represented by the empty type (an empty set of values).This kind of analysis is not only theoretical, it actually happens with SBCL:
Moreover, here we can see the derived type of
THE-THING
:The
NIL
type means: this functions will not return successfully. For example, in SBCL, theerror
function has the following declared type:... because it never returns a value but signals a condition. You can use
NIL
in function type declarations explicitely if you want. By the way, please notice that SBCL only gives a warning and still compiles your code: you can call the function and it will trigger the dynamic checks that will lead you to the debugger, etc.NIL
implies the absence of "normal" termination. If the body of your function is(loop)
, the return type is alsoNIL
. But sometimes you can return normally but you do not have any value to return (likevoid
in C). That is why(values &optional)
is more appropriate as a type declaration when you return no value successfully. This is done by using the(values)
expression in your code. The&optional
keyword in the type declaration is necessary because the type(values)
would allow you to return more values (see comments).Is
nil
necessary?Some implementations might not care about
nil
. Since you wonder if we could remove it without causing any harm, maybe trying to remove thenil
type from an implementation could be an nice experiment, to see how much does effectively break in practice.Foreign types
Regarding foreign types, I doubt it can be used like in Haskell, while conforming to the spec, because:
This is purely speculative, but maybe a variable (or an array) of type
nil
allocated by a CL runtime could be handled to some foreign code (using implementation-specific means). This is not how it is done in CFFI.The case of
nil
arraysI don't know why (maybe just because there is no check) some implementations like SBCL allow arrays of elements of type
nil
, defined as follows:However, one can't access an element of such an array:
The following type error is caught: "The value 5 is not of type (MOD 4611686018427387901)." Moreover, the backtrace contains the following line, where the actual content of the array is seen containing random values:
Accessing uninitialized arrays has undefined consequences (see comments). Consequences are undefined even if the actual data is only referenced and not effectively used for its content, as in:
The example is taken from the "Issue UNINITIALIZED-ELEMENTS Writeup".
I see no reason to allow the creation of arrays of type NIL in the first place, apart maybe from allocating (useless?) memory. As said above, maybe there is an implementation-specific, totally not portable way for some lisp to use such arrays, and pass them to foreign functions, but I doubt it.
Finally, in Maclisp's manual (note that Maclisp predates Common Lisp), it is said that NIL arrays can be used as arrays of type T but make the garbage collector behave as if no objects were referenced. However, here is what the manual says about them: