eee: když si definuju tohle:
data Value = VInt Int | VDouble Double | VStrint Text | VBool Boolean | VObject (Dict Text Value) | VNone
-- Čti: Typ Value může nabývat hodnot "VInt s parametrem celé číslo", "VDouble s parametrem double" etc.
-- To VInt, VDouble je de-fakto "tag"
Tak to je přesně ten typ, který se používá v JS, Pythonu etc. Všechny funkce potom budou typu ve stylu:
f: Value -> Value
-- Čti: vstupem do funkce je hodnota typu Value, výstupem je hodnota typu Value
No a třeba sčítání by se třeba definovalo jako:
(+) :: Value -> Value -> Value
(+) (VInt i) (VInt j) = VInt (i + j)
(+) (VDouble i) (VDouble j) = VDouble (i + j)
(+) (VInt i) (VDouble j) = VDouble (toDouble i + j)
(+) _ _ = error "crash"
Takže pak mám v programu jeden typ a hotovo, chová se to přesně stejně jako dynamické jazyky (teď ignoruju pure/non-pure, to je další level).
Ve staticky typovaném jazyku můžu udělat něco, co v tom dynamickém nemůžeš: Můžu tu funkci omezit. Můžu říct: tahle funkce může vrátit jenom Int. A nic jiného.
Tady si imho odporuješ. Klidně přistoupím na tezi, že dynamický jazyk má jediný součtový typ, proč ne. Ale pořád v dynamickém jazyku platí, že funkce může vracet hodnoty různých datových typů (nebo variant v tvé terminologii). To ovšem rozporuješ o kus výše, kde uvádíš, že funkce vrací jeden součtový typ. Takže buď to není totožné s fungováním dynamických jazyků a nebo součtový typ vrací hodnoty různých datových typů (variant v tvé terminologii). Obojí současně platit nemůže, vyber si, co platí.
Varianta typu není typ. Prvek "enumu" taky není zvláštní typ - je to hodnota typu toho enumu.
Představ si součtový typ jako "Enum", kdy ale jednotlivé volby jsou "structy" (v C je tomu dost blízký union, ale překladač nevynucuje to "nebo"). Ad absurdum by se to dalo Enumem simulovat takhle (v C++?):
enum JsValue = { JSNull, JSTrue, JSFalse, Int0, Int1, Int2, Int3, Int4,.... }
Jak vidíš, typ je fakt jeden (JsValue), ale klidně ta funkce (i v C) může vrátit "null", "true", "false", 0, 1.... atd.