Type detection

From Rosetta Code
Type detection 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.

This draft task needs a purpose, a description and some way to tell whether examples satisfy or do not satisfy it.


Task

Show a function/procedure that processes a block of text by printing it.

The function takes one parameter (ideally) that describes the text.

Demonstrate by calling the function twice, each time passing in a different type.


This can be done with pattern matching, multi-methods, dynamic type detection, structure(s) with a tag, etc.

The objective is write a [e.g. library] function that processes text from multiple sources (such as a string/char *, socket, file, etc).

If not practical, show how the caller would coerce a type that can be passed to the library function.

J

Presumably this satisfies the task requirements...

<lang J> echo 'one' one

  echo 1

1</lang>

JavaScript

[1]

console.log(typeof('foo')); // Returns string
console.log(typeof(12345)); // Returns number

Kotlin

<lang scala>// version 1.0.6 fun showType(a: Any) = when (a) {

       is Int    -> println("'$a' is an integer")
       is Double -> println("'$a' is a double")
       is Char   -> println("'$a' is a character")
       else      -> println("'$a' is some other type")
   }

fun main(args: Array<String>) {

   showType(5)
   showType(7.5)
   showType('d')
   showType(true)

}</lang>

Output:
'5' is an integer
'7.5' is a double
'd' is a character
'true' is some other type

OASYS Assembler

<lang oasys_oaa>

The following method checks if a global variable or property is an
object type. Does not work with locals and arguments.

[&OBJ#,^]

 ,^<,^<<    ; Remember old value
 ,^<*>      ; Create new object
 ,^<<DES    ; Destroy the object
 ,^<<EX     ; Check if variable has been cleared
 />1RF      ; It is clear
 :>0RF      ; It is not clear

</lang>


Perl 6

Perl 6 is a dynamic language that has gradual, duck typing. It provides introspection methods through its comprehensive MOP (Meta Object Protocol) making it easy to do type detection, subroutine signatures and multi-dispatch. Perl 6 types have two general flavors: content types and container types. Different container types have varying restrictions on what sort of content they can contain and in return provide specialized methods to operate on those contents. Content types give the compiler hints on how to best handle the information, what storage requirements it may have, what operators will work with it, etc.

This is really a very broad and kind of hand-wavey overview of Perl 6 types. For much more indepth coverage see Perl 6 Synopsis S02: Bits and Pieces: Built-In Data Types

<lang perl6>sub type ($t) { say $t.perl, "\tis type: ", $t.WHAT }

  1. some content types

.&type for 1, 2.0, 3e0, 4i, π, Inf, NaN, 'String';

  1. some primitive container types

.&type for $, [ ], @, { }, %, (5 .. 7), (8 ... 10), /0/, {;}, sub {}, ( );

  1. undefined things

.&type for Any, Nil;

  1. user defined types

class my-type { };

my my-type $object;

$object.&type;</lang>

Output:
1	is type: (Int)
2.0	is type: (Rat)
3e0	is type: (Num)
<0+4i>	is type: (Complex)
3.14159265358979e0	is type: (Num)
Inf	is type: (Num)
NaN	is type: (Num)
"String"	is type: (Str)
Any	is type: (Any)
$[]	is type: (Array)
$[]	is type: (Array)
{}	is type: (Hash)
{}	is type: (Hash)
5..7	is type: (Range)
(8, 9, 10).Seq	is type: (Seq)
/0/	is type: (Regex)
-> ;; $_? is raw { #`(Block|61385680) ... }	is type: (Block)
sub () { #`(Sub|62948936) ... }	is type: (Sub)
$()	is type: (List)
Any	is type: (Any)
Nil	is type: Nil
my-type	is type: (my-type)

Phix

Phix builtin type tests are: integer(), atom(), string(), sequence(), and object() - the latter returns true unless arg is unassigned. <lang Phix>procedure showtype(object o) string t = iff(atom(o)?iff(integer(o)?"integer":"atom")

                     :iff(string(o)?"string":"sequence"))
   ?{t,o}

end procedure

showtype(5) showtype(7.5) showtype("string") showtype({5,7.5,"string"})</lang>

Output:
{"integer",5}
{"atom",7.5}
{"string","string"}
{"sequence",{5,7.5,"string"}}

PHP

[2]

echo gettype('foo'); // Returns string
echo gettype(12345); // Returns integer

Specific tester functions

PowerShell

In PowerShell everything is an object and all objects have the GetType() method: <lang PowerShell> [string]$str = "123" $str.GetType() </lang>

Output:
IsPublic IsSerial Name                                     BaseType     
-------- -------- ----                                     --------                                                                                                                  
True     True     String                                   System.Object

<lang PowerShell> [int]$int = $str -as [int] $int.GetType() </lang>

Output:
IsPublic IsSerial Name                                     BaseType        
-------- -------- ----                                     --------                                                                                                                  
True     True     Int32                                    System.ValueType

Python

Built-in function type()

>>> type('foo')
<class 'str'>
>>> type(12345)
<class 'int'>

Testing types

>>> type('foo') is str
True
>>> type(123.0) is not int
True
>>> type([]) is list
True
>>> type({}) is dict
True

Racket

Hopefully you can see how to extend the code to add all sorts of other types. If I did this, I’d swamp the task page. A good list of types supported/provided by Racket can be found in the Typed Racket reference: http://docs.racket-lang.org/ts-reference/type-ref.html

<lang racket>#lang racket

(require racket/undefined)

(define fooer<%> (interface ())) (define foo% (class* object% (fooer<%>)

              (super-new)))

(struct my-tree (l v r))

-----------------------------------------------------------------------------

(define (n.t f)

 (list f (regexp-replace #rx"\\?" (symbol->string (object-name f)) "")))
listed in the order (as close as) shown in
http://docs.racket-lang.org/guide/datatypes.html (section numbers next to
some entries)

(define type-tests.names

 `(,@(map n.t
          (list boolean? immutable? ; 3.1
                ))
   ;; the famous scheme numerical tower
   ,@(map n.t ; 3.2
          (list number? complex? real? rational? integer? exact-integer?
                exact-nonnegative-integer? exact-positive-integer?
                inexact-real? fixnum? flonum? double-flonum? single-flonum?
                zero? positive? negative? odd? even? exact? inexact?))
   ,@(map n.t
          (list char? ; 3.3 --- there are also char-alphabetic? etc -- but they're not
                      ;         types as such
                string? ; 3.4
                byte? bytes? ; 3.5
                symbol? ; 3.6
                keyword? ; 3.7
                pair? null? list? ; 3.8
                vector? ; 3.9
                hash? hash-equal? hash-eqv? hash-eq? hash-weak? ; 3.10
                box? ; 3.11
                void? ; 3.12
                ))
   ,(list (λ (v) (eq? v undefined)) "undefined") ; 3. 12
   ;; now we move to http://docs.racket-lang.org/reference/data.html
   ;; for section numbering
   ,@(map n.t
          (list
           regexp? pregexp? byte-regexp? byte-pregexp? ; 4.7
           stream? sequence? ; 4.14
           dict? ; 4.15
           set-equal? set-eqv? set-eq? set? set-mutable? set-weak? ; 4.16
           continuation? procedure? ; 4.17            
           ))
   ;; class/interface testing
   ,(list (λ (v) (is-a? v object%)) "object%")
   ,(list (λ (v) (is-a? v foo%)) "foo%")
   ,(list (λ (v) (is-a? v fooer<%>)) "fooer<%>")
   ;; more types from reference (sections are top-level, mostly)
   ,@(map n.t
          (list
           syntax? ; 3.
           my-tree? ; 5.
           exn? exn:fail? exn:fail:filesystem? ; 10.2
           promise? ; 10.3
           ))
   ;; there's all sorts of other types to test!
   ))

(define (->type-names v)

 (let ((rv (for/list ((t.n (in-list type-tests.names))
                      #:when (with-handlers
                                 ((exn? (λ (x) #f)))
                               ((car t.n) v))) (cadr t.n))))
   (if (null? rv) (list "UNKNOWN") rv)))

(module+ test

 (require xml/xml)
 (define test-values
   (list 3.+4.i 3+4i (- pi) pi 0. 0 -0.5 0.5 -1/3 1/3
         -12345678909876543210123456789 12345678909876543210123456788 -132 133
         #\t #\null
         "" "monkeys" "\u03BB"
         -1 255 256
         #"" #"nibble"
         'hello '||
         '#:woo
         '() '(1 . 2) '(3) '(5 6)
         #() #(1) #("foo" 2 'bar)
         (make-hash)
         (make-hasheq)
         (make-hasheqv)
         (hash)
         (hasheq)
         (hasheqv)
         (make-weak-hash)
         (make-weak-hasheq)
         (make-weak-hasheqv)     
         (make-immutable-hash)
         (make-immutable-hasheq)
         (make-immutable-hasheqv)
         (box "x")
         (void)
         undefined
         #rx".*" #px"3?" #rx#"t.m" #px#".i."
         (in-vector #(1 2 3)) (stream 1 2 3)
         #hash((a . "apple")) #("apple" "binana") '("apple" "binana")
          '((a . "apple") (b . "binana"))
          (set 1 2 3) (seteq 1 2 3) (seteqv 1 2 3)
          (mutable-set 1 2 3) (mutable-seteq 1 2 3) (mutable-seteqv 1 2 3)
          (weak-set 1 2 3) (weak-seteq 1 2 3) (weak-seteqv 1 2 3)
          + (λ (x) #t) (call/cc (λ (k) k))
          (new object%) (new foo%)
          #'(xxy zzy)
          (my-tree (my-tree #f 1 #f) 2 #f)
          (with-handlers ((exn? values)) (error 'aargh!))
          (with-handlers ((exn? values))
            (file->string "/tmp/there-is-no-way-this-file-exists---surely?"))
          (delay 3)
         ))
 ;; tempted to print a cross-reference table, but that would be too wide for RC (maybe)
 (write-xexpr #:insert-newlines? #f
  `(table (thead (tr (th "Value [~s]") (th "->type-name")))
          "\n"
          (tbody ,@(map (λ (v) `(tr "\n" (td ,(~s v))
                                    (td ,(string-join (->type-names v) ", "))))
                        test-values)))))

</lang>

Output:

The following table is generated:

<thead></thead> <tbody></tbody>
Value [~s]->type-name
3.0+4.0inumber, complex, inexact
3+4inumber, complex, exact
-3.141592653589793number, complex, real, rational, inexact-real, flonum, double-flonum, negative, inexact
3.141592653589793number, complex, real, rational, inexact-real, flonum, double-flonum, positive, inexact
0.0number, complex, real, rational, integer, inexact-real, flonum, double-flonum, zero, even, inexact
0number, complex, real, rational, integer, exact-integer, exact-nonnegative-integer, fixnum, zero, even, exact, byte, sequence
-0.5number, complex, real, rational, inexact-real, flonum, double-flonum, negative, inexact
0.5number, complex, real, rational, inexact-real, flonum, double-flonum, positive, inexact
-1/3number, complex, real, rational, negative, exact
1/3number, complex, real, rational, positive, exact
-12345678909876543210123456789number, complex, real, rational, integer, exact-integer, negative, odd, exact
12345678909876543210123456788number, complex, real, rational, integer, exact-integer, exact-nonnegative-integer, exact-positive-integer, positive, even, exact, sequence
-132number, complex, real, rational, integer, exact-integer, fixnum, negative, even, exact
133number, complex, real, rational, integer, exact-integer, exact-nonnegative-integer, exact-positive-integer, fixnum, positive, odd, exact, byte, sequence
#\tchar
#\nulchar
""immutable, string, sequence
"monkeys"immutable, string, sequence
"λ"immutable, string, sequence
-1number, complex, real, rational, integer, exact-integer, fixnum, negative, odd, exact
255number, complex, real, rational, integer, exact-integer, exact-nonnegative-integer, exact-positive-integer, fixnum, positive, odd, exact, byte, sequence
256number, complex, real, rational, integer, exact-integer, exact-nonnegative-integer, exact-positive-integer, fixnum, positive, even, exact, sequence
#""immutable, bytes, sequence
#"nibble"immutable, bytes, sequence
hellosymbol
||symbol
#:wookeyword
()null, list, stream, sequence, dict
(1 . 2)pair
(3)pair, list, stream, sequence
(5 6)pair, list, stream, sequence
#()immutable, vector, sequence, dict
#(1)immutable, vector, sequence, dict
#("foo" 2 (quote bar))immutable, vector, sequence, dict
#hash()hash, hash-equal, sequence, dict
#hasheq()hash, hash-eq, sequence, dict
#hasheqv()hash, hash-eqv, sequence, dict
#hash()immutable, hash, hash-equal, sequence, dict
#hasheq()immutable, hash, hash-eq, sequence, dict
#hasheqv()immutable, hash, hash-eqv, sequence, dict
#<hash>hash, hash-equal, hash-weak, sequence, dict
#<hash>hash, hash-eq, hash-weak, sequence, dict
#<hash>hash, hash-eqv, hash-weak, sequence, dict
#hash()immutable, hash, hash-equal, sequence, dict
#hasheq()immutable, hash, hash-eq, sequence, dict
#hasheqv()immutable, hash, hash-eqv, sequence, dict
#&"x"box
#<void>void
#<undefined>undefined
#rx".*"regexp
#px"3?"regexp, pregexp
#rx#"t.m"byte-regexp
#px#".i."byte-regexp, byte-pregexp
#<sequence>sequence
#<stream>stream, sequence
#hash((a . "apple"))immutable, hash, hash-equal, sequence, dict
#("apple" "binana")immutable, vector, sequence, dict
("apple" "binana")pair, list, stream, sequence
((a . "apple") (b . "binana"))pair, list, stream, sequence, dict
#<set: 1 3 2>stream, sequence, set-equal, set
#<seteq: 1 2 3>stream, sequence, set-eq, set
#<seteqv: 1 2 3>stream, sequence, set-eqv, set
#<mutable-set: 1 2 3>sequence, set-equal, set-mutable
#<mutable-seteq: 1 2 3>sequence, set-eq, set-mutable
#<mutable-seteqv: 1 2 3>sequence, set-eqv, set-mutable
#<weak-set: 1 3 2>sequence, set-equal, set-weak
#<weak-seteq: 1 2 3>sequence, set-eq, set-weak
#<weak-seteqv: 1 2 3>sequence, set-eqv, set-weak
#<procedure:+>procedure
#<procedure:...pe-detection.rkt:113:13>procedure
#<continuation>continuation, procedure
#(struct:object)object%
#(struct:object:foo% ...)object%, foo%, fooer<%>
#<syntax:D:\Users\tim\Dropbox\hacking\rosettacode\type-detection.rkt:117:13 (xxy zzy)>syntax
#<my-tree>my-tree
#(struct:exn:fail "error: aargh!" #<continuation-mark-set>)exn, exn:fail
#(struct:exn:fail:filesystem "file-size: file not found\n path: D:/tmp/there-is-no-way-this-file-exists---surely?" #<continuation-mark-set>)exn, exn:fail, exn:fail:filesystem
#<promise:...e/type-detection.rkt:122:11>promise

REXX

These are some of the tests that can be performed on REXX variables (values) to determine which   type   they are. <lang rexx>/*REXX program displays what "type" a variable is (based on the variable's value). */ signal on noValue /*trap for undefined REXX variables. */ y=1938  ; call showType y /* ╔═════════════════════════════════╗ */ y=77.1  ; call showType y /* ║ All REXX variables are stored as║ */ y=  ; call showType y /* ║ character strings, even numbers.║ */ y=' '  ; call showType y /* ║ If a variable string is numeric,║ */ y='abc'  ; call showType y /* ║ all comparisons (IF statements) ║ */ y='ABC'  ; call showType y /* ║ that are made with numbers are ║ */ y='aBc'  ; call showType y /* ║ compared numerically. If not ║ */ y='1515'x  ; call showType y /* ║ numeric, the string is compared ║ */ y='10 11'x  ; call showType y /* ║ character by character after ║ */ y='00 0001'b ; call showType y /* ║ leading blanks are removed. ║ */ y='1'b  ; call showType y /* ╚═════════════════════════════════╝ */

                         call showType yyy      /*note:  the variable YYY is undefined.*/

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ noValue: say ' REXX variable ' condition('D') " is undefined."; exit /*──────────────────────────────────────────────────────────────────────────────────────*/ showType: procedure; parse arg x; arg xU /*obtain true value and also uppercase.*/

          @='      value is';          say @ x
                                       say @ 'of length'  length(x)
          if  x==               then say @ "null."
          if  x\== & x=       then say @ "all blank."
          if  datatype(x, 'N')    then say @ "numeric (decimal)."
                                  else say @ "a character string (not numeric)."
          if  datatype(x, 'W')    then say @ "an integer (a whole number)."
          if  datatype(x, 'N') &,
             \datatype(x, 'W')    then say @ "not an integer."
          if  datatype(x, 'N') &,
              pos('E', xu)\==0    then say @ "a number in exponential format."
          if  datatype(x, 'A')    then say @ "an alphanumeric string."
          if  datatype(x, 'U')    then say @ "all uppercase (Latin) letters."
          if  datatype(x, 'L')    then say @ "all lowercase (Latin) letters."
          if \datatype(x, 'L') &,
             \datatype(x, 'U') &,
              datatype(x, 'M')    then say @ "of mixed case (Latin) letters."
          if  datatype(x, 'B')    then say @ "binary."
          if  datatype(x, 'X')    then say @ "hexadecimal."
          if  datatype(x, 'S')    then say @ "a REXX symbol."
          say copies('b1'x,50)                  /*fence character used for a separator.*/
          return</lang>

output   when using the internal data:

      value is 1938
      value is of length 4
      value is numeric (decimal).
      value is an integer (a whole number).
      value is an alphanumeric string.
      value is hexadecimal.
      value is a REXX symbol.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is 77.1
      value is of length 4
      value is numeric (decimal).
      value is not an integer.
      value is a REXX symbol.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is
      value is of length 0
      value is null.
      value is a character string (not numeric).
      value is binary.
      value is hexadecimal.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is
      value is of length 3
      value is all blank.
      value is a character string (not numeric).
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is abc
      value is of length 3
      value is a character string (not numeric).
      value is an alphanumeric string.
      value is all lowercase (Latin) letters.
      value is hexadecimal.
      value is a REXX symbol.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is ABC
      value is of length 3
      value is a character string (not numeric).
      value is an alphanumeric string.
      value is all uppercase (Latin) letters.
      value is hexadecimal.
      value is a REXX symbol.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is aBc
      value is of length 3
      value is a character string (not numeric).
      value is an alphanumeric string.
      value is of mixed case (Latin) letters.
      value is hexadecimal.
      value is a REXX symbol.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is §§
      value is of length 2
      value is a character string (not numeric).
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is ►◄
      value is of length 2
      value is a character string (not numeric).
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is ☺
      value is of length 1
      value is a character string (not numeric).
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      value is ☺
      value is of length 1
      value is a character string (not numeric).
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
      REXX variable  YYY  is undefined.

zkl

<lang zkl>fcn processText(data_or_fileName){ // unknown

  if (data_or_fileName.isType(String)) // == .isType("")
     data_or_fileName=File(data_or_fileName,"rb").read(); //-->Data
  text:=data_or_fileName.text;  //-->String
  doTheActualTextProcessing(text);

} fcn doTheActualTextProcessing(text){ println(text) }</lang> If an int is passed in, (123).text --> "123", other objects might throw an exception.

How to use: <lang zkl>processText("foo.txt"); processText(Data(Void,"This is some text")); // fake up a class that holds a string: cs:=class{ var text }; cs.text="this is more text"; processText(cs);</lang>

Output:
this is foo.txt

This is some text
this is more text