Records in Haskell are notoriously difficult to compose; many solutions have been proposed. Vinyl lies in the space of library-level approaches, and addresses polymorphism, extensibility, effects and strictness. I describe how Vinyl approaches record families over arbitrary key spaces using a Tarski universe construction, as well as a method for immersing each field of a record in a chosen effect modality. Also discussed are presheaves, sheaves and containers.
15. Roll your own in Haskell
data (s :: Symbol) ::: (t :: ∗) = Field
16. Roll your own in Haskell
data (s :: Symbol) ::: (t :: ∗) = Field
data Rec :: [∗] → ∗ where
17. Roll your own in Haskell
data (s :: Symbol) ::: (t :: ∗) = Field
data Rec :: [∗] → ∗ where
RNil :: Rec ’[]
18. Roll your own in Haskell
data (s :: Symbol) ::: (t :: ∗) = Field
data Rec :: [∗] → ∗ where
RNil :: Rec ’[]
(:&) :: !t → !(Rec rs) → Rec ((s ::: t) ’: rs)
19. Roll your own in Haskell
data (s :: Symbol) ::: (t :: ∗) = Field
data Rec :: [∗] → ∗ where
RNil :: Rec ’[]
(:&) :: !t → !(Rec rs) → Rec ((s ::: t) ’: rs)
class s ∈ (rs :: [∗])
20. Roll your own in Haskell
data (s :: Symbol) ::: (t :: ∗) = Field
data Rec :: [∗] → ∗ where
RNil :: Rec ’[]
(:&) :: !t → !(Rec rs) → Rec ((s ::: t) ’: rs)
class s ∈ (rs :: [∗])
class ss ⊆ (rs :: [∗]) where
cast :: Rec rs → Rec ss
21. Roll your own in Haskell
data (s :: Symbol) ::: (t :: ∗) = Field
data Rec :: [∗] → ∗ where
RNil :: Rec ’[]
(:&) :: !t → !(Rec rs) → Rec ((s ::: t) ’: rs)
class s ∈ (rs :: [∗])
class ss ⊆ (rs :: [∗]) where
cast :: Rec rs → Rec ss
(=:) : s ::: t → t → Rec ’[s ::: t]
22. Roll your own in Haskell
data (s :: Symbol) ::: (t :: ∗) = Field
data Rec :: [∗] → ∗ where
RNil :: Rec ’[]
(:&) :: !t → !(Rec rs) → Rec ((s ::: t) ’: rs)
class s ∈ (rs :: [∗])
class ss ⊆ (rs :: [∗]) where
cast :: Rec rs → Rec ss
(=:) : s ::: t → t → Rec ’[s ::: t]
(⊕) : Rec ss → Rec ts → Rec (ss ++ ts)
23. Roll your own in Haskell
data (s :: Symbol) ::: (t :: ∗) = Field
data Rec :: [∗] → ∗ where
RNil :: Rec ’[]
(:&) :: !t → !(Rec rs) → Rec ((s ::: t) ’: rs)
class s ∈ (rs :: [∗])
class ss ⊆ (rs :: [∗]) where
cast :: Rec rs → Rec ss
(=:) : s ::: t → t → Rec ’[s ::: t]
(⊕) : Rec ss → Rec ts → Rec (ss ++ ts)
lens : s ::: t ∈ rs ⇒ s ::: t → Lens’ (Rec rs) t
28. Old
We relied on a single representation for keys as pairs of
strings and types.
data Rec :: [∗] → ∗ where
RNil :: Rec ’[]
(:&) :: !t → !(Rec rs) → Rec ((s ::: t) ’: rs)
29. Proposed
We put the keys in an arbitrary type (kind) and describe
their semantics with a function el (for elements).
data Rec :: [∗] → ∗ where
RNil :: Rec ’[]
(:&) :: !t → !(Rec rs) → Rec ((s ::: t) ’: rs)
30. Proposed
We put the keys in an arbitrary type (kind) and describe
their semantics with a function el (for elements).
data Rec :: (el :: k → ∗) → (rs :: [k]) → ∗ where
RNil :: Rec el ’[]
(:&) :: ∀(r :: k) (rs’:: [k]). !(el r) → !(Rec el rs) → Rec (r ’: rs’)
31. Proposed
We put the keys in an arbitrary type (kind) and describe
their semantics with a function el (for elements).
data Rec :: (k → ∗) → [k] → ∗ where
RNil :: Rec el ’[]
(:&) :: !(el r) → !(Rec el rs) → Rec (r ’: rs)
32. Actual
Type families are not functions, but in many cases can
simulate them using Richard Eisenberg’s technique
outlined in Defunctionalization for the win.
data TyFun :: ∗ → ∗ → ∗
33. Actual
Type families are not functions, but in many cases can
simulate them using Richard Eisenberg’s technique
outlined in Defunctionalization for the win.
data TyFun :: ∗ → ∗ → ∗
type family (f :: TyFun k l → ∗) $ (x :: k) :: l
34. Actual
Type families are not functions, but in many cases can
simulate them using Richard Eisenberg’s technique
outlined in Defunctionalization for the win.
data TyFun :: ∗ → ∗ → ∗
type family (f :: TyFun k l → ∗) $ (x :: k) :: l
data Rec :: (TyFun k ∗ → ∗) → [k] → ∗ where
RNil :: Rec el ’[]
(:&) :: !(el $ r) → !(Rec el rs) → Rec el (r ’: rs)
35. Example
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
36. Example
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
data SLabel :: Label → ∗ where
SHome :: SLabel Home
SWork :: SLabel Work
37. Example
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
data SLabel :: Label → ∗
38. Example
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
data SLabel :: Label → ∗
data SAddrKeys :: AddrKeys → ∗ where
SName :: SAddrKeys Name
SPhone :: SLabel l → SAddrKeys (Phone l)
SEmail :: SLabel l → SAddrKeys (Email l)
39. Example
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
data SLabel :: Label → ∗
data SAddrKeys :: AddrKeys → ∗
40. Example
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
data SLabel :: Label → ∗
data SAddrKeys :: AddrKeys → ∗
data ElAddr :: (TyFun AddrKeys ∗) → ∗ where
ElAddr :: ElAddr el
41. Example
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
data SLabel :: Label → ∗
data SAddrKeys :: AddrKeys → ∗
data ElAddr :: (TyFun AddrKeys ∗) → ∗ where
ElAddr :: ElAddr el
type instance ElAddr $ Name = String
type instance ElAddr $ (Phone l) = [N]
type instance ElAddr $ (Email l) = String
type AddrRec rs = Rec ElAddr rs
43. Sugared example
import Data.Singletons as S
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
S.genSingletons [ ’’Label, ’’AddrKeys ]
44. Sugared example
import Data.Singletons as S
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
S.genSingletons [ ’’Label, ’’AddrKeys ]
makeUniverse ’’AddrKeys ”ElAddr”
45. Sugared example
import Data.Singletons as S
data Label = Home | Work
data AddrKeys = Name | Phone Label | Email Label
S.genSingletons [ ’’Label, ’’AddrKeys ]
makeUniverse ’’AddrKeys ”ElAddr”
type instance ElAddr $ Name = String
type instance ElAddr $ (Phone l) = [N]
type instance ElAddr $ (Email l) = String
type AddrRec rs = Rec ElAddr rs
46. Example records
bob :: AddrRec [Name, Email Work]
bob = SName =: ”Robert W. Harper”
⊕ SEmail SWork =: ”rwh@cs.cmu.edu”
47. Example records
bob :: AddrRec [Name, Email Work]
bob = SName =: ”Robert W. Harper”
⊕ SEmail SWork =: ”rwh@cs.cmu.edu”
jon :: AddrRec [Name, Email Work, Email Home]
jon = SName =: ”Jon M. Sterling”
⊕ SEmail SWork =: ”jon@fobo.net”
⊕ SEmail SHome =: ”jon@jonmsterling.com”
50. Recovering HList
data Id :: (TyFun k k) → ∗ where
type instance Id $ x = x
type HList rs = Rec Id rs
51. Recovering HList
data Id :: (TyFun k k) → ∗ where
type instance Id $ x = x
type HList rs = Rec Id rs
ex :: HList [Z, Bool, String]
ex = 34 :& True :& ”vinyl” :& RNil
52. Validating records
validateName :: String → Either Error String
validateEmail :: String → Either Error String
validatePhone :: [N] → Either Error [N]
53. Validating records
validateName :: String → Either Error String
validateEmail :: String → Either Error String
validatePhone :: [N] → Either Error [N]
*unnnnnhhh...*
54. Validating records
validateName :: String → Either Error String
validateEmail :: String → Either Error String
validatePhone :: [N] → Either Error [N]
*unnnnnhhh...*
validateContact
:: AddrRec [Name, Email Work]
→ Either Error (AddrRec [Name, Email Work])
56. Effects inside records
data Rec :: (TyFun k ∗ → ∗) → [k] → ∗ where
RNil :: Rec el ’[]
(:&) :: !(el $ r) → !(Rec el rs) → Rec el (r ’: rs)
57. Effects inside records
data Rec :: (TyFun k ∗ → ∗) → (∗ → ∗) → [k] → ∗ where
RNil :: Rec el f ’[]
(:&) :: !(f (el $ r)) → !(Rec el f rs) → Rec el f (r ’: rs)
58. Effects inside records
data Rec :: (TyFun k ∗ → ∗) → (∗ → ∗) → [k] → ∗ where
RNil :: Rec el f ’[]
(:&) :: !(f (el $ r)) → !(Rec el f rs) → Rec el f (r ’: rs)
59. Effects inside records
data Rec :: (TyFun k ∗ → ∗) → (∗ → ∗) → [k] → ∗ where
RNil :: Rec el f ’[]
(:&) :: !(f (el $ r)) → !(Rec el f rs) → Rec el f (r ’: rs)
(=:) : Applicative f ⇒ sing r → el $ r → Rec el f ’[r]
k =: x = pure x :& RNil
60. Effects inside records
data Rec :: (TyFun k ∗ → ∗) → (∗ → ∗) → [k] → ∗ where
RNil :: Rec el f ’[]
(:&) :: !(f (el $ r)) → !(Rec el f rs) → Rec el f (r ’: rs)
(=:) : Applicative f ⇒ sing r → el $ r → Rec el f ’[r]
k =: x = pure x :& RNil
(⇐): sing r → f (el $ r) → Rec el f ’[r]
k ⇐ x = x :& RNil
69. Record effect operators
( ) :: Rec el (Lift (→) f g) rs → Rec el f rs → Rec el g rs
rtraverse :: Applicative h ⇒
(∀ x. f x → h (g x)) →
Rec el f rs → h (Rec el g rs)
70. Record effect operators
( ) :: Rec el (Lift (→) f g) rs → Rec el f rs → Rec el g rs
rtraverse :: Applicative h ⇒
(∀ x. f x → h (g x)) →
Rec el f rs → h (Rec el g rs)
rdist :: Applicative f ⇒ Rec el f rs → f (Rec el Identity rs)
rdist = rtraverse (fmap Identity)
75. Laziness as an effect
newtype Identity a = Identity { runIdentity :: a }
data Thunk a = Thunk { unThunk :: a }
76. Laziness as an effect
newtype Identity a = Identity { runIdentity :: a }
data Thunk a = Thunk { unThunk :: a }
type PlainRec el rs = Rec el Identity rs
77. Laziness as an effect
newtype Identity a = Identity { runIdentity :: a }
data Thunk a = Thunk { unThunk :: a }
type PlainRec el rs = Rec el Identity rs
type LazyRec el rs = Rec el Thunk rs
83. Concurrent records with Async
newtype Concurrently a
= Concurrently { runConcurrently :: IO a }
84. Concurrent records with Async
newtype Concurrently a
= Concurrently { runConcurrently :: IO a }
( $ ) :: (∀ a. f a → g a) → Rec el f rs → Rec el g rs
85. Concurrent records with Async
newtype Concurrently a
= Concurrently { runConcurrently :: IO a }
( $ ) :: (∀ a. f a → g a) → Rec el f rs → Rec el g rs
bobConcurrently :: Rec el Concurrently [Name, Email Work]
bobConcurrently = Concurrently $ fetchBob
86. Concurrent records with Async
newtype Concurrently a
= Concurrently { runConcurrently :: IO a }
( $ ) :: (∀ a. f a → g a) → Rec el f rs → Rec el g rs
bobConcurrently :: Rec el Concurrently [Name, Email Work]
bobConcurrently = Concurrently $ fetchBob
concurrentBob :: IO (PlainRec el [Name, Email Work])
concurrentBob = runConcurrently (rdist bobConcurrently)
102. Vinyl: beyond records?
Records and corecords are finite products and sums
respectively.
Rec(ElU ; F; rs) :≡
U|rs
F ◦ ElU
103. Vinyl: beyond records?
Records and corecords are finite products and sums
respectively.
Rec(ElU ; F; rs) :≡
U|rs
F ◦ ElU
CoRec(ElU ; F; rs) :≡
U|rs
F ◦ ElU
104. Vinyl: beyond records?
Records and corecords are finite products and sums
respectively.
Rec(ElU ; F; rs) :≡
U|rs
F ◦ ElU
CoRec(ElU ; F; rs) :≡
U|rs
F ◦ ElU
Data(ElU ; F; rs) :≡ WU|rs
F ◦ ElU
105. Vinyl: beyond records?
Records and corecords are finite products and sums
respectively.
Rec(ElU ; F; rs) :≡
U|rs
F ◦ ElU
CoRec(ElU ; F; rs) :≡
U|rs
F ◦ ElU
Data(ElU ; F; rs) :≡ WU|rs
F ◦ ElU
Codata(ElU ; F; rs) :≡ MU|rs
F ◦ ElU
111. Presheaves
A presheaf on some space X is a functor
O(X)op
→ Type, where O is the category of open sets of
X for whatever topology you have chosen.
114. Topologies on some space X
What are the open sets on X?
The empty set and X are open sets
115. Topologies on some space X
What are the open sets on X?
The empty set and X are open sets
The union of open sets is open
116. Topologies on some space X
What are the open sets on X?
The empty set and X are open sets
The union of open sets is open
Finite intersections of open sets are open
119. Records are presheaves
Let O = P, the discrete topology
Then records on a universe X give rise to a presheaf
R: subset inclusions are taken to casts from larger to
smaller records
120. Records are presheaves
Let O = P, the discrete topology
Then records on a universe X give rise to a presheaf
R: subset inclusions are taken to casts from larger to
smaller records
for U ∈ P(X) R(U) :≡
U
ElX|U : Type
121. Records are presheaves
Let O = P, the discrete topology
Then records on a universe X give rise to a presheaf
R: subset inclusions are taken to casts from larger to
smaller records
for U ∈ P(X) R(U) :≡
U
ElX|U : Type
for i : V → U R(i) :≡ cast : R(U) → R(V )
123. Records are sheaves
For a cover U = i Ui on X, then:
R(U) i R(Ui) i,j R(Ui ∩ Uj)
e p
q
is an equalizer, where
e = λr.λi. castUi
(r)
p = λf.λi.λj. castUi∩Uj
(f(i))
q = λf.λi.λj. castUi∩Uj
(f(j))
124. Records are sheaves
For a cover U = i Ui on X, then:
R(U) i R(Ui) i,j R(Ui ∩ Uj)
Γ
e
m
!u
p
q
where
e = λr.λi. castUi
(r)
p = λf.λi.λj. castUi∩Uj
(f(i))
q = λf.λi.λj. castUi∩Uj
(f(j))
135. Future work
Complete: formalization of records with topologies as
presheaves in Coq
In Progress: formalization of records as sheaves
Future: extension to dependent records using
extensional type theory and realizability (Nuprl,
MetaPRL)