Sum data type

From Rosetta Code
Sum data type 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.

Data Structure
This illustrates a data structure, a means of storing data within a program.

You may see other such structures in the Data Structures category.


Task

Create a sum data type:

A sum data type is a data structure used to hold a value that could take on several different, but fixed, types. Only one of the types can be in use at any one time.

Sum data types are considered an algebraic data type and are also known as tagged union, variant, variant record, choice type, discriminated union, disjoint union or coproduct.

Related task
See also




ALGOL 68

Translation of: OCaml

Algol 68's UNION MODE allows the definition of items which can have different types. <lang algol68>MODE LEAF = INT; MODE NODE = STRUCT( TREE left, TREE right ); MODE TREE = UNION( VOID, LEAF, REF NODE );

TREE t1 = LOC NODE := ( LEAF( 1 ), LOC NODE := ( LEAF( 2 ), LEAF( 3 ) ) );</lang>

Note that assignment/initialisation of UNION items is just of a matter of specifying the assigned/initial value, as above; however to use the value requires a CASE clause, such as in the example below (which would print "node", given the above declarations).

<lang algol68>CASE t1

 IN (REF NODE n): print( ( "node",     newline ) )
  , (    LEAF l): print( ( "leaf ", l, newline ) )
  , (    VOID  ): print( ( "empty",    newline ) )

ESAC</lang>

Factor

This is accomplished by defining a tuple with only one slot. The slot should have a class declaration that is a union class. This ensures that the slot may only contain an object of a class that is in the union. A convenient way to do this is with an anonymous union, as in the example below. An explicit UNION: definition may also be used.

In the example below, we define a pseudo-number tuple with one slot that can hold either a number (a built-in class) or a numeric-string — a class which we have defined to be any string that can parse as a number using the string>number word. <lang factor>USING: accessors kernel math math.parser strings ;

PREDICATE: numeric-string < string string>number >boolean ; TUPLE: pseudo-number { value union{ number numeric-string } } ; C: <pseudo-number> pseudo-number  ! constructor

5.245 <pseudo-number>  ! ok "-17" >>value  ! ok "abc42" >>value  ! error</lang>

Go

Go doesn't natively support sum types, though it's not difficult to create one (albeit verbosely) as the following example shows.

Normally, the IPAddr type (and associated types/methods) would be placed in a separate package so its 'v' field couldn't be accessed directly by code outside that package. However here, for convenience, we place it in the 'main' package. <lang go>package main

import (

   "errors"
   "fmt"

)

type (

   IpAddr struct{ v interface{} }
   Ipv4   = [4]uint8
   Ipv6   = string

)

var zero = Ipv4{}

func NewIpAddr(v interface{}) (*IpAddr, error) {

   switch v.(type) {
   case Ipv4, Ipv6:
       return &IpAddr{v}, nil
   default:
       err := errors.New("Type of value must either be Ipv4 or Ipv6.")
       return nil, err
   }

}

func (ip *IpAddr) V4() (Ipv4, error) {

   switch ip.v.(type) {
   case Ipv4:
       return ip.v.(Ipv4), nil
   default:
       err := errors.New("IpAddr instance doesn't currently hold an Ipv4.")
       return zero, err
   }

}

func (ip *IpAddr) SetV4(v Ipv4) {

   ip.v = v

}

func (ip *IpAddr) V6() (Ipv6, error) {

   switch ip.v.(type) {
   case Ipv6:
       return ip.v.(Ipv6), nil
   default:
       err := errors.New("IpAddr instance doesn't currently hold an Ipv6.")
       return "", err
   }

}

func (ip *IpAddr) SetV6(v Ipv6) {

   ip.v = v

}

func check(err error) {

   if err != nil {
       fmt.Println(err)
   }

}

func main() {

   v4 := Ipv4{127, 0, 0, 1}
   ip, _ := NewIpAddr(v4)
   home, _ := ip.V4()
   fmt.Println(home)
   v6 := "::1"
   ip.SetV6(v6)
   loopback, _ := ip.V6()
   fmt.Println(loopback)
   _, err := ip.V4()
   check(err)
   rubbish := 6
   ip, err = NewIpAddr(rubbish)
   check(err)

}</lang>

Output:
[127 0 0 1]
::1
IpAddr instance doesn't currently hold an Ipv4.
Type of value must either be Ipv4 or Ipv6.

Julia

Julia allows the creation of union types. <lang Julia>Using Sockets # for IP types

   julia> MyUnion = Union{Int64, String, Float64, IPv4, IPv6}
   Union{Float64, Int64, IPv4, IPv6, String}
   julia> arr = MyUnion[2, 4.8, ip"192.168.0.0", ip"::c01e:fc9a", "Hello"]
   5-element Array{Union{Float64, Int64, IPv4, IPv6, String},1}:
    2
    4.8
     ip"192.168.0.0"
     ip"::c01e:fc9a"
     "Hello"

</lang>

OCaml

<lang ocaml>type tree = Empty

         | Leaf of int
         | Node of tree * tree

let t1 = Node (Leaf 1, Node (Leaf 2, Leaf 3))</lang>

REXX

The REXX language is untyped,   it is up to the program to decide if it's valid and how to deal with an invalid structure. <lang rexx>/*REXX pgm snipette validates a specific type of data structure, an IP v4 address (list)*/ ip= 127 0 0 1 if val_ipv4(ip) then say 'valid IPV4 type: ' ip

                else say '***error***  invalid IPV4 type: '    ip

...

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ val_ipv4: procedure; parse arg $; if words($)\==4 | arg()\==1 then return 0

           do j=1  for 4;   _=word($, j);    #=datatype(_, 'W');    L= length(_)
           if verify(_, 0123456789)\==0  |  \#  | _<0  |  _>255  |  L>3  then return 0
           end   /*j*/
         return 1                               /*returns true (1) if valid, 0 if not. */</lang>


Rust

<lang rust>enum IpAddr {

   V4(u8, u8, u8, u8),
   V6(String),

}

let home = IpAddr::V4(127, 0, 0, 1);

let loopback = IpAddr::V6(String::from("::1"));</lang>

zkl

zkl is untyped - it is up to the container to decide if it wants to deal with a type or not. <lang zkl>ip:=List(127,0,0,1); addrs:=Dictionary("ip",ip);</lang> <lang zkl>class Addr{

  fcn init(addr){
     var ip = addr;
     if(not List.isType(addr)) throw(Exception.TypeError);
  }

} ip:=Addr(List(127,0,0,1)); Addr(127,0,0,1); // TypeError : Invalid type Addr(List("abc")); // doesn't fail, would need more error checking ip.ip=L(192,168,1,1); // this doesn't type check either</lang>