Tuesday, April 22, 2025

Algebraic Types

Most programming languages have definable types.  The most common algebraic types are the structure and the tagged union.  

A structure (also known as a record) is an "and" type, also known as a "product" type.  This is because it contains a list of fields, each potentially of a different type, so you have this and that and the other.  All the fields are simultaneously valid and available.

type
    andType: struct of:
        intField: int,
        floatField: float,
        stringField: string

var 
    structVar: andType

structVar.intField = 42
structVar.floatField = 3.14
structVar.stringField = "Howdy!"

assert structVar is andType               # base type
assert structVar.stringField is string    # field type


A tagged union is an "or" type, also known as a "sum" type.  This is because it can store this or that or the other.  The tag keeps track of which subtype is currently valid.  The strength of the tagged union is in allowing it to contain multiple types, to be used for different purposes.

type
    Tags: enum of:
        intTag1, intTag2, floatTag, stringTag

    OrType1: union of thisTag: Tags:
        intTag1, intTag2: int,    # shared subtype
        floatTag: float,
        stringTag: string

var 
    orVar1: OrType1

orVar1.intTag1 = 42          # int
orVar1 = 21                  # int
orVar1 = 3.14                # error!  Can't mix types
orVar1.thisTag = stringTag   # reset tag, clear data
orVar1 = "Howdy!"            # string
orVar1.intTag2 = 21          # int
orVar1 += 12                 # ok
orVar1.thisTag = intTag1     # reset tag, keep data

assert orVar1 == 33
assert orVar1 is OrType1            # base type
assert type of orVar1 is int        # subtype
assert orVar1.thisTag == Tags.intTag1


A different sort of sum type is a typed union.  You don't need an enum for the discriminator, but you can't have two or more of the same type, either.  The strength of a typed union is the simplicity of use.

type
    OrType2: union of type:
        int, float, string

var
    orVar2: OrType2

orVar2 = 42                  # int
orVar2 += 3.14               # error!  Can't mix types.
orVar2 = 6.28                # float
orVar2 = "Howdy!"            # string

assert orVar2 is OrType2           # base type
assert type of orVar2 is string    # subtype


There is also a more primitive type:  the untagged union.  An untagged union is like a typed union but without space for remembering the subtype, so you have to keep track of what it contains separately.  It is more flexible, but much more dangerous in practical use.


type
    Dangerous: union of:
        int, float

var
    intOrFloat: Dangerous

intOrFloat = 3.14            # float
intOrFloat = 42              # int
intOrFloat += 2.5            # silent error!  
    # This compiles and runs, but has unpredictable results.

assert intOrFloat is Dangerous       # base type
assert type of intOrFloat is int     # error!  
    # The subtype is not recorded.


2 comments:

  1. Off topic, everything changed, for me, at your Gab site. Different color, only the Korean posting and nothing else.

    ReplyDelete
    Replies
    1. Haven't said much on Gab lately. It's not been the same since they went pay to play.

      Delete

I reserve the right to remove egregiously profane or abusive comments, spam, and anything else that really annoys me. Feel free to agree or disagree, but let's keep this reasonably civil.