joost@de-vries․name
	joost-de-vries
	jouke
UNION	TYPES
&	
LITERAL	SINGLETON
TYPES
ING	Zandkasteel	-	Amsterdam



THREE	ACTS
1.	 What	are	they?	Why	are	they	useful?
2.	 How	to	use	them	in	Scala	right	now
3.	 Using	them	in	Dotty
①
UNION	TYPES	&
LITERAL	SINGLETON	TYPES
What	are	they?
Why	are	they	useful?
LITERAL	SINGLETON
TYPES
We	know	singleton	types:	every	scala	singleton	has	a	type
object	S{		
}
def	g(s:S.type)=s
//	g:	(s:	S.type)S$
								
A	singleton	type	is	a	type	with	just	one	instance
LITERAL	SINGLETON
TYPES
But	what	we'd	like	to	express	is	literals	in	types.	For
instance	in	Spire
forAll	{	x:	Ranged[Int,	1,	100]	=>
		val	n:	Int	=	x.value	//	guaranteed	to	be	1	to	100
}
LITERAL	SINGLETON
TYPES
And	in	Scala.js
These	are	some	of	the	motivations	mentioned	in	
				trait	HTMLCanvasElement	extends	HTMLElement	{
						def	getContext(contextId:	String):	Any
						def	getContext(contextId:	"2d"):	CanvasRenderingContext2D
				}
				val	anyContext	=	canvas.getContext("webgl")
				val	context2D	=	canvas.getConttext("2d")
								
SIP-42
UNION	TYPES
It's	or	for	types
Terminology	from	set	theory
INTERSECTION	TYPES
It's	and	for	types
TRANSLATION
And ⇒ Or
∧ ⇒ ∨
Conjunction ⇒ Disjunction
Intersection ⇒ Union
Co-product ⇒ Tuple	/	Product	/	HList
Can	be	confusing
INTERSECTION	TYPES
We	almost	have	intersection	types	in	Scala
Usually	used	for	mixins
But	A	with	B	doesn't	equal	B	with	A
				val	loggablePerson	=	new	Person	with	Loggable()
UNION	TYPES	ARE
USEFUL
It's	Either[A,B]
for	any	nr	of	types
without	boxing
/**
	*	If	'padding'	is	a	string,	then	'padding'	is	appended	to	the	left	side.
	*	If	'padding'	is	a	number,	then	that	number	of	spaces	is	added	to	the	left	side.
	*/
function	padLeft(value:	string,	padding:	string	|	number)	=	{
				//	...
}
padLeft("hello",	true)	//	doesn't	compile
UNION	TYPES
Either	can't	do	that
interface	Bird	{
				fly()
				layEggs()
}
interface	Fish	{
				swim()
				layEggs()
}
function	getSmallPet():	Fish	|	Bird	{			/*	...	*/				}
let	pet	=	getSmallPet()
pet.layEggs()	//	okay
//	pet.swim()				doesn't	compile
USE	UNION	TYPES	FOR	
NON	NULLABLE	TYPES
let	foo:	string	=	null	//	Error!
let	foo:	string	|	null	=	null	//	Okay!
Using	the	strictNullCheck	compiler	option
UNION	OF	LITERAL	TYPES
What	an	awesome	way	
to	express	enumerations!
type	Digit	=	0	|	1	|	2	|	3	|	4	|	5	|	6	|	7	|	8	|	9;
let	nums:	Digit[]	=	[1,	2,	4,	8];
nums.push(16)	//	Doesn't	compile
②
UNION	TYPES	&
LITERAL	SINGLETON	TYPES
How	to	use	them	in	Scala	right	now
UNION	TYPES	IN	SCALA
RIGHT	NOW
PROGRAMMING	IS	LIKE	LOGIC
if it is thursday there's a scala meeting
if there's a scala meeting the term 'monad' will come up
it is thursday
-------------------------------------------------------
the term 'monad' will come up
PROGRAMMING	IS	LIKE	LOGIC
it is thursday → there's a scala meeting
there's a scala meeting → the term 'monad' will come up
it is thursday
-------------------------------------------------------
the term 'monad' will come up
PROGRAMMING	IS	LIKE	LOGIC
Thursday → ScalaMeeting
ScalaMeeting → Monad
Thursday
-------------------------------------------------------
Monad
PROGRAMMING	IS	LIKE	LOGIC
val	f:		Thursday	⇒	ScalaMeeting
val	g:		ScalaMeeting	⇒	Monad
val	t:		Thursday
val	m:	Monad	=	g(f(t))
								
The	implementation	of	the	function	is	the	proof	of	its	type
IN	LOGIC
We	can	create	or	out	of	and,	not
A	or	B	⇔		not	(	not	A	and	not	B)
IN	LOGIC
We	can	create	or	out	of	and,	not
A	⋁	B	⇔		¬	(	¬A	⋀	¬B)
SCALA	TO	LOGIC
(A	with	B)	<:	A (A	∧	B)	⇒	A
(A	with	B)	<:	B (A	∧	B)	⇒	B
A	<:	(A	∨	B) A	⇒	(A	∨	B)
B	<:	(A	∨	B) B	⇒	(A	∨	B)
CREATING	OR
FROM	AND,	NOT
(A	∨	B)	⇔	¬(¬A	∧	¬B)
becomes
(A	∨	B)	=:=	¬[¬[A]	with	¬[B]]
So	where	do	we	get	not?
UNION	TYPES
IN	3	LINES	OF	CODE
Is	Miles	Sabin	cool	or	what?
An	instance	of	A	<:<	B	witnesses	that	A	is	a	subtype	of	B
type	¬[A]	=	A	=>	Nothing
type	∨[T,	U]	=	¬[¬[T]	with	¬[U]]
type	¬¬[A]	=	¬[¬[A]]
implicitly[¬¬[Int]	<:<	(Int	∨	String)]
//	res0:	<:<[¬¬[Int],∨[Int,String]]	=	<function1>	
		implicitly[¬¬[Double]	<:<	(Int	∨	String)]
//	error:	Cannot	prove	that	¬¬[Double]	<:<	∨[Int,String].
LET'S	USE	OUR	UNION	TYPE
def	padLeft[T](value:	String,	padding:	T)
						(implicit	ev:	¬¬[T]	<:<	(Int	∨	String))	=	"some	string"
padLeft("hello",	3)	//compiles
//	padLeft("hello",	true)	doesn't	compile:
//	error:	Cannot	prove	that	Test5.¬[Test5.¬[Boolean']]	<:<	Test5.∨[Int,	String].
//	padLeft("hello",	true)
UNION	TYPES
IN	3	LINES	OF	CODE
Put	the	evidence	in	a	path	dependent	type	λ
specific	to	our	types	T	and	U
type	|∨|[T,	U]	=	{	type	λ[X]	=	¬¬[X]	<:<	(T	∨	U)	}
def	padLeft2[T](value:String,	padding:T)
						(implicit	ev:	(Int	|∨|	String)#λ[T])	=	"some	string"
USE	THE	IMPLEMENTATION	IN
SHAPELESS
Union	types	are	called	Coproducts	in	Shapeless
Literal	types	(peano	numbers)	are	used	in	Sized
collections
③
UNION	TYPES	&
LITERAL	SINGLETON	TYPES
Using	them	in	Dotty
UNION	TYPES
What	we	want	to	achieve
/**
	*	If	'padding'	is	a	string,	then	'padding'	is	appended	to	the	left	side.
	*	If	'padding'	is	a	number,	then	that	number	of	spaces	is	added	to	the	left	side.
	*/
function	padLeft(value:	string,	padding:	string	|	number)	=	{
				//	...
}
padLeft("hello",	true)	//	doesn't	compile
UNION	TYPES	IN	DOTTY
/**
	*	If	'padding'	is	a	string,	then	'padding'	is	appended	to	the	left	side.
	*	If	'padding'	is	a	number,	then	that	number	of	spaces	is	added	to	the	left	side.
	*/
def	padLeft(value:	String,	padding:	String	|	Int)	=	{
				//	...
}
padLeft("hello",	true)
//	error:	type	mismatch:
//		found			:	Boolean(true)
//		required:	String	|	Int
//	padLeft("hello",	true)
//																		^
								
Cool!
UNION	TYPES	IN	DOTTY
class	Bird()	{
				def	fly()={}
				def	layEggs()={}
}
class	Fish()	{
				def	swim()={}
				def	layEggs()={}
}
def	getSmallPet()	=	if(new	Random().nextBoolean)	new	Fish()	else	new	Bird()
val	p	=	getSmallPet()
p.layEggs()
error:	value	layEggs	is	not	a	member	of	Object(p)
p.layEggs()
		^
								
Huh?
UNION	TYPES	IN	DOTTY
import	dotty.language.keepUnions
class	Bird()	{
				def	fly()={}
				def	layEggs()={}
}
class	Fish()	{
				def	swim()={}
				def	layEggs()={}
}
def	getSmallPet()	=	if(new	Random().nextBoolean)	new	Fish()	else	new	Bird()
val	p	=	getSmallPet()
p.layEggs()		//	compiles
								 Because	of	performance
of	large	nrs	of	unioned	types
UNION	TYPES	PLUS	SINGLETON
TYPES	IN	DOTTY
		object	Monday
		object	Tuesday
		//	...
		type	Weekday	=	Monday.type	|	Tuesday.type	|	Wednesday.type	|	Thursday.type	|	Friday.type
		type	Weekend	=	Saturday.type	|	Sunday.type
		type	AnyDay		=	Weekday	|	Weekend
				println("Monday	is	weekday:	"	+	Monday.isInstanceOf[Weekday])
				println("Saturday	is	weekend:	"	+	Saturday.isInstanceOf[Weekend])
				println("Sunday	is	weekday:	"	+	Sunday.isInstanceOf[Weekday])
				(Monday:	AnyDay)	match	{
						case	_:	Weekend	=>	println("shouldn't	match")
				}
								
Works	again	since	7	days	ago
UNION	TYPES	PLUS	SINGLETON
TYPES	IN	DOTTY
Bug!
		type	Weekday	=	"Monday"	|	"Tuesday"	|	"Wednesday"	|	"Thursday"	|	"Friday"
		type	Weekend	=	"Saturday"	|	"Sunday"
		type	AnyDay		=	Weekday	|	Weekend
		val	d:Weekday	=	"Sunday"		//	compiles?!
								
Currently	unions	of	literal	types	are	widened
to	String	in	this	case
UNION	TYPES	PLUS	SINGLETON
TYPES	IN	DOTTY
Same	problem
type	Digit	=	0	|	1	|	2	|	3	|	4	|	5	|	6	|	7	|	8	|	9	
def	format(ds:Seq[Digit])={	}
format(Seq(4,9,13))	//	shouldn't	compile
UNION	TYPES	PLUS	SINGLETON
TYPES	IN	DOTTY
DOTTY
It	really	is,	like	the	site	says,	pre	beta
It's	easy	to	try	out	with	the	sbt-dotty	plugin
Although	there	are	no	snapshot	builds	yet
But:	syntax	highlighting	in	the	console!
Kleurtjes!
Fin

Union Types and Literal Singleton Types in Scala (and Typescript)