Implicit type conversion: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Perl 6}}: various clarifications)
Line 215: Line 215:
</lang>
</lang>


User defined types will support implicit casting if the object has Bridge method that tells it how to do so, or if the operators in question supply multiple dispatch variants that all for coercions. Note that Perl 6 does not support implicit assignment coercion to typed variables. However, different-sized storage types (int16, int32, int64, for example) are not considered different types, and such assignment merely enforces a constraint that will throw an exception if the size is exceeded. (The calculations on the right side of the assignment are done in an arbitrarily large type such as Int.)
User defined types will support implicit casting if the object has Bridge method that tells it how to do so, or if the operators in question supply multiple dispatch variants that allow for coercions. Note that Perl 6 does not support implicit assignment coercion to typed variables. However, different-sized storage types (int16, int32, int64, for example) are not considered different types, and such assignment merely enforces a constraint that will throw an exception if the size is exceeded. (The calculations on the right side of the assignment are done in an arbitrarily large type such as Int.)


Types may be explicitly cast by using a bridge method (.Int, .Rat, .Str, whatever) or by using a coercion operator:
Types may be explicitly cast by using a bridge method (.Int, .Rat, .Str, whatever) or by using a coercion operator:

Revision as of 17:14, 26 April 2013

Implicit type conversion is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Some programming languages have implicit type conversion. Type conversion is also known as coercion.

For example: <lang algol68>COMPL z := 1;</lang>Here the assignment ":=" implicitly converts the integer 1, to the complex number in the programming language ALGOL 68.

The alternative would be to explicitly convert a value from one type to another, using a function or some other mechanism (e.g. an explicit cast).

The following code samples demonstrate the various type conversions in each language, and give an example of a implicit type conversion path from the smallest possible variable size to the largest possible variable size. (Where the size of the underlying variable's data strictly increases).

In strong typed languages some types are actually mutually incompatible. In this case the language may have disjoint type conversion paths, or even branching type conversion paths. (Where it occurs in a specific language, it is demonstrated in the code samples below.)

Languages that don't support any implicit type conversion are detailed in the /Omit categories at the bottom of this page.

Indicate if the language supports user defined type conversion definitions. And give an example of such a definition. (E.g. define an implicit type conversion from real to complex numbers, or from char to an array of char of length 1.)

ALGOL 68

Works with: ALGOL 68 version Revision 1
Works with: ALGOL 68G version Any - tested with release algol68g-2.6.

File: implicit_type_conversion.a68<lang algol68>#!/usr/bin/a68g --script #

  1. -*- coding: utf-8 -*- #

main:(

  1. a representative sample of builtin types #
   BOOL b; INT bool width = 1;
   BITS bits; LONG BITS lbits;
   BYTES bytes; LONG BYTES lbytes; # lbytes := bytes is not illegal #
   [bits width]BOOL array bool;
   [long bits width]BOOL long array bool;
   CHAR c; INT char width = 1;
   STRING array char;
   SHORT SHORT INT ssi; SHORT INT si; INT i; LONG INT li; LONG LONG INT lli;
   SHORT SHORT REAL ssr; SHORT REAL sr; REAL r; LONG REAL lr; LONG LONG REAL llr;
   SHORT SHORT COMPL ssz; SHORT COMPL sz; COMPL z; LONG COMPL lz; LONG LONG COMPL llz;
   INT long long compl width = 2 * long long real width;
   STRUCT (BOOL b, CHAR c, INT i, REAL r, COMPL z)cbcirz;  # NO implicit casting #
   REF []INT rai;
   FORMAT long long real fmt = $g(-0,long long real width-2)$;
   FORMAT long long compl fmt = $f(long long real fmt)"+"f(long long real fmt)"i"$;
  1. type conversion stating points #
   b := TRUE;
   c := "1";
   ssi := SHORT SHORT 1234; i := 1234;
  1. a representative sample of implied casts for subtypes of personality INT/REAL/COMPL #
    si:=ssi;
     i:=ssi;  i:=si;
    li:=ssi; li:=si;  li:=i;
   lli:=ssi;lli:=si; lli:=i;  lli:=li;
   ssr:=ssi;
    sr:=ssr; sr:=ssi; sr:=si;
     r:=ssr;  r:=sr;   r:=ssi;  r:=si;   r:=i;
    lr:=ssr; lr:=sr;  lr:=r;   lr:=ssi; lr:=si;  lr:=i;   lr:=li;
   llr:=ssr;llr:=sr; llr:=r;  llr:=lr; llr:=ssi;llr:=si; llr:=i;  llr:=li; llr:=i;
   ssz:=ssr;ssz:=ssi;
    sz:=ssz; sz:=ssr; sz:=sr;  sz:=ssi; sz:=si;
     z:=ssz;  z:=sz;   z:=ssr;  z:=sr;   z:=r;    z:=ssi;  z:=si;   z:=i;
    lz:=ssz; lz:=sz;  lz:=z;   lz:=ssr; lz:=sr;  lz:=r;   lz:=lr;  lz:=ssi; lz:=si;  lz:=i;   lz:=li;
   llz:=ssz;llz:=sz; llz:=z;  llz:=lz; llz:=ssr;llz:=sr; llz:=r;  llz:=lr; llz:=r;  llz:=ssi;llz:=si; llz:=i;  llz:=li; llz:=lli;
  1. conversion branch SHORT SHORT INT => LONG LONG COMPL #
  2. a summary result, using the longest sizeof increasing casting path #
   printf((long long compl fmt,llz:=(llr:=(lr:=(i:=(si:=ssi)))),$
           $l"  was increasingly cast"$,
           $" from "g(-0)$, long long compl width, long long real width, long real width,
                           int width, short int width, short short int width, $" digits"$,
           $" from "g(-0)l$,ssi ));
  1. conversion branch BITS => []BOOL #
   bits := 16rf0f0ff00;
   lbits := bits;
   printf(($g$,"[]BOOL := LONG BITS := BITS - implicit widening: ",array bool := bits, $l$));
  1. conversion branch BYTES => []CHAR #
   bytes := bytes pack("0123456789ABCDEF0123456789abcdef");
   long array bool := LONG 2r111;
   printf(($g$,"[]CHAR := LONG BYTES := BYTES - implicit widening: ",array char := bytes, $l$));
  1. deproceduring conversion branch PROC PROC PROC INT => INT #
   PROC pi = INT: i;
   PROC ppi = PROC INT: pi;
   PROC pppi = PROC PROC INT: ppi;
   printf(($g$,"PROC PROC PROC INT => INT - implicit deprocceduring^3: ",pppi, $l$));
  1. dereferencing conversion branch REF REF REF INT => INT #
   REF INT ri := i;
   REF REF INT rri := ri;
   REF REF REF INT rrri := rri;
   printf(($g$,"REF REF REF INT => INT - implicit dereferencing^3: ",rrri, $l$));
  1. rowing conversion branch INT => []INT => [,]INT => [,,]INT #
  2. a representative sample of implied casts, type pointer #
   rai := ai; # starts at the first element of ai #
   rai := i;  # an array of length 1 #
   FLEX[0]INT ai := i;
   FLEX[0,0]INT aai := ai;
   FLEX[0,0,0]INT aaai := aai;
   printf(($g$,"INT => []INT => [,]INT => [,,]INT - implicit rowing^3: ",aaai, $l$));
  1. uniting conversion branch UNION(VOID, INT) => UNION(VOID,INT,REAL) => UNION(VOID,INT,REAL,COMPL) #
   UNION(VOID,INT) ui := i;
   UNION(VOID,INT,REAL) uui := ui;
   UNION(VOID,INT,REAL,COMPL) uuui := uui;
   printf(($g$,"INT => UNION(VOID, INT) => UNION(VOID,INT,REAL,COMPL) - implicit uniting^3: ",(uuui|(INT i):i), $l$));
 SKIP

)</lang>Output:

1234.0000000000000000000000000000000000000000000000000000000000000+.0000000000000000000000000000000000000000000000000000000000000i
  was increasingly cast from 126 from 63 from 28 from 10 from 10 from 10 digits from 1234
[]BOOL := LONG BITS := BITS - implicit widening: TTTTFFFFTTTTFFFFTTTTTTTTFFFFFFFF
[]CHAR := LONG BYTES := BYTES - implicit widening: 0123456789ABCDEF0123456789abcdef
PROC PROC PROC INT => INT - implicit deprocceduring^3:       +1234
REF REF REF INT => INT - implicit dereferencing^3:       +1234
INT => []INT => [,]INT => [,,]INT - implicit rowing^3:       +1234
INT => UNION(VOID, INT) => UNION(VOID,INT,REAL,COMPL) - implicit uniting^3:       +1234

C

<lang c>#include <stdio.h> main(){ /* a representative sample of builtin types */

   unsigned char uc; char c;
   enum{e1, e2, e3}e123;
   short si; int i; long li;
   unsigned short su; unsigned u; unsigned long lu;
   float sf; float f; double lf; long double llf;
   union {char c; unsigned u; int i; float f; }ucuif;  /* manual casting only */
   struct {char c; unsigned u; int i; float f; }scuif; /* manual casting only */
   int ai[99];
   int (*rai)[99];
   int *ri;
   uc = '1';

/* a representitive sample of implied casts for subtypes of personality int/float */

   c=uc;
   si=uc; si=c;
   su=uc; su=c; su=si;
   i=uc;  i=c;  i=si;  i=su;
   e123=i; i=e123;
   u=uc;  u=c;  u=si;  u=su;  u=i;
   li=uc; li=c; li=si; li=su; li=i; li=u;
   lu=uc; lu=c; lu=si; lu=su; lu=i; lu=u; lu=li;
   sf=uc; sf=c; sf=si; sf=su; sf=i; sf=u; sf=li; sf=lu;
   f=uc;  f=c;  f=si;  f=su;  f=i;  f=u;  f=li;  f=lu;  f=sf;
   lf=uc; lf=c; lf=si; lf=su; lf=i; lf=u; lf=li; lf=lu; lf=sf; lf=f;
   llf=uc;llf=c;llf=si;llf=su;llf=i;llf=u;llf=li;llf=lu;llf=sf;llf=f;llf=lf;

/* ucuif = i; no implied cast; try: iucuif.i = i */ /* ai = i; no implied cast; try: rai = &i */ /* a representitive sample of implied casts, type pointer */

   rai = ai; /* starts at the first element of ai */
   ri = ai;  /* points to the first element of ai */

/* a summary result, using the longest sizeof increasing casting path */

   printf("%LF was increasingly cast from %d from %d from %d from %d from %d bytes from '%c'\n",
          llf=(lf=(i=(si=c))), sizeof llf, sizeof lf, sizeof i, sizeof si,sizeof c, c);

}</lang> Output:

49.000000 was increasingly cast from 12 from 8 from 4 from 2 from 1 bytes from '1'

Perl 6

Perl 6 was designed with a specific goal of maximizing the principle of DWIM (Do What I Mean) while simultaneously minimizing the principle of DDWIDM (Don't Do What I Don't Mean). Implicit type conversion is a natural and basic feature.

Variable names in Perl 6 are prepended with a sigil. The most basic variable container type is a scalar, with the sigil dollar sign: $x. A single scalar variable in list context will be converted to a list of one element regardless of the variables structure. (A scalar variable may be bound to any object, including a collective object. A scalar variable is always treated as a singular item, regardless of whether the object is essentially composite or unitary. There is no implicit conversion from singular to plural; a plural object within a singular container must be explicitly decontainerized somehow. Use of a subscript is considered sufficiently explicit though.)

The type of object contained in a scalar depends on how you assign it and how you use it.

<lang perl6>my $x;

$x = 1234; say $x.WHAT; # (Int) Integer $x = 12.34; say $x.WHAT; # (Rat) Rational $x = 1234e-2; say $x.WHAT; # (Num) Floating point Number $x = 1234+i; say $x.WHAT; # (Complex) $x = '1234'; say $x.WHAT; # (Str) String $x = (1,2,3,4); say $x.WHAT; # (Parcel) Parentheses Cell $x = [1 2 3 4]; say $x.WHAT; # (Array) $x = 1 .. 4; say $x.WHAT; # (Range) $x = (1 => 2); say $x.WHAT; # (Pair) $x = {1 => 2}; say $x.WHAT; # (Hash) $x = {1, 2}; say $x.WHAT; # (Block) $x = sub {1}; say $x.WHAT; # (Sub) Code Reference $x = True; say $x.WHAT; # (Bool) Boolean</lang>


Objects may be converted between various types many times during an operation. Consider the following line of code.

<lang perl6>say :16(([+] 1234.ords).sqrt.floor ~ "beef");</lang>

In English: Take the floor of the square root of the sum of the ordinals of the digits of the integer 1234, concatenate that number with the string 'beef', interpret the result as a hexadecimal number and print it.

Broken down step by step:

<lang perl6>my $x = 1234; say $x, ' ', $x.WHAT; # 1234 (Int) $x = 1234.ords; say $x, ' ', $x.WHAT; # 49 50 51 52 (List) $x = [+] 1234.ords; say $x, ' ', $x.WHAT; # 202 (Int) $x = ([+] 1234.ords).sqrt; say $x, ' ', $x.WHAT; # 14.2126704035519 (Num) $x = ([+] 1234.ords).sqrt.floor; say $x, ' ', $x.WHAT; # 14 (Int) $x = ([+] 1234.ords).sqrt.floor ~ "beef"; say $x, ' ', $x.WHAT; # 14beef (Str) $x = :16(([+] 1234.ords).sqrt.floor ~ "beef"); say $x, ' ', $x.WHAT; # 1359599 (Int)</lang>


Some types are not implicitly converted. For instance, you must explicitly request and cast to Complex numbers and FatRat numbers. (A normal Rat number has a denominator that is limited to 64 bits, with underflow to floating point to prevent performance degradation; a FatRat, in contrast, has an unlimited denominator size, and can chew up all your memory if you're not careful.)

<lang perl6>$x = (-1).sqrt; say $x, ' ', $x.WHAT; # NaN (Num) $x = (-1).Complex.sqrt; say $x, ' ', $x.WHAT; # 6.12323399573677e-17+1i (Complex)

$x = (22/7) * 2; say $x, ' ', $x.WHAT; # 6.285714 (Rat) $x /= 10**10; say $x, ' ', $x.WHAT; # 0.000000000629 (Rat) $x /= 10**10; say $x, ' ', $x.WHAT; # 6.28571428571429e-20 (Num)

$x = (22/7).FatRat * 2; say $x, ' ', $x.WHAT; # 6.285714 (FatRat) $x /= 10**10; say $x, ' ', $x.WHAT; # 0.000000000629 (FatRat) $x /= 10**10; say $x, ' ', $x.WHAT; # 0.0000000000000000000629 (FatRat) </lang>

User defined types will support implicit casting if the object has Bridge method that tells it how to do so, or if the operators in question supply multiple dispatch variants that allow for coercions. Note that Perl 6 does not support implicit assignment coercion to typed variables. However, different-sized storage types (int16, int32, int64, for example) are not considered different types, and such assignment merely enforces a constraint that will throw an exception if the size is exceeded. (The calculations on the right side of the assignment are done in an arbitrarily large type such as Int.)

Types may be explicitly cast by using a bridge method (.Int, .Rat, .Str, whatever) or by using a coercion operator:

    + or -      numify
    ~           stringify
    ? or !      boolify
    i (postfix) complexify
    $()         singularize
    @()         pluralize
    %()         hashify

Tcl

Virtually all type conversions in Tcl are implicit. A value is an integer (or a string, or a list, or …) because that is how you are using it. The only true explicit type conversion operations are some of the functions in the expression sub-language (int(), double(), etc.).

Integer conversion
<lang tcl>set value "123"

incr someVar $value

  1. $value will now hold an integer (strictly, one of many integer-related types) with value 123</lang>
Float conversion
<lang tcl>set value "1.23"

expr {$value + 3.5}

  1. $value will now hold a double-precision IEEE floating point number that is (approx.) 1.23</lang>
String conversion
<lang tcl>set value [expr {123 + 456}]

string length $value

  1. $value will now hold a string (of length 3)</lang>
List conversion
<lang tcl>set value {a b c d}

llength $value

  1. $value will now hold a list (of length 4)</lang>
Dictionary conversion
<lang tcl>set value {a b c d}

dict size $value

  1. $value will now hold a dictionary (of size 2)</lang>

There are many other value types (command names, variable names, subcommand index names, etc.) but user code would not normally seek to explicitly convert to those.

Defining a new type requires writing an extension to Tcl in C (or whatever the host programming language is, so Java for JTcl); the interfaces for doing this are not directly exposed to the Tcl script level because they require direct memory access, which Tcl normally does not permit in order to promote overall process stability.