Sum data type
Data Structure
This illustrates a data structure, a means of storing data within a program.
- 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
- Array
- Associative array: Creation, Iteration
- Collections
- Compound data type
- Doubly-linked list: Definition, Element definition, Element insertion, List Traversal, Element Removal
- Linked list
- Queue: Definition, Usage
- Set
- Singly-linked list: Element definition, Element insertion, List Traversal, Element Removal
- Stack
ALGOL 68
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>
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.
OCaml
<lang ocaml>type tree = Empty
| Leaf of int | Node of tree * tree
let t1 = Node (Leaf 1, Node (Leaf 2, Leaf 3))</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>