--- /dev/null
+---
+title: Row types and modules
+date: 2023-04-01
+draft: false
+tags: [haskell, modules, records, row types]
+---
+
+I was thinking about modules a few years ago. One of the directions was the intersection of modules with row types. I decided to write something down to keep the idea.
+
+OCaml consists of two languages:
+
+- term language — to construct programs with terms and types;
+
+- module language — to create signatures, modules, and functors.
+
+1ML merges these levels — modules become values. I noticed that modules behave in a similar way/correspond to records. Since the Service/Handle pattern in Haskell is about using a record to represent an interface and to have different implementations by filling the record's fields, what if we use row types on module level? If row types might be seen as an alternative to records, then it might be possible to represent row types values on module level as an alternative to modules.
+
+
+| Term language | | Module language |
+|---------------|--|--------------------|
+| Records |~>| Modules |
+| Row types |~>|? |
+
+
+I don't want to imagine an artificial language for this purpose and will use Haskell with the Handle pattern, [extending the previous post](/posts/implementations-of-the-handle-pattern/).
+
+```haskell
+import Data.Row
+
+import qualified WindProvider
+import qualified TemperatureProvider
+
+type Location = String
+type Day = String
+type WeatherData = String
+
+type Methods = ("getWeatherData" .== (Location -> Day -> IO WeatherData))
+ .+ WindProvider.Methods
+ .+ TemperatureProvider.Methods
+
+-- We can remove an item from the record
+type WindAndTemperatureMethods = Methods .- "getWeatherData"
+
+test1 :: Rec WindAndTemperatureMethods
+ -> Rec (WindProvider.Methods .+ TemperatureProvider.Methods)
+test1 = id
+
+-- It's possible to override methods
+type MethodsWithUpdatedWeatherData =
+ ("getWeatherData" .== (Location -> Day -> IO Int)) .// Methods
+
+test2 :: Rec MethodsWithUpdatedWeatherData
+ -> Rec (
+ ("getWeatherData" .== (Location -> Day -> IO Int))
+ .+ WindProvider.Methods .+ TemperatureProvider.Methods
+ )
+test2 = id
+
+-- And to remove them by taking a row difference
+type MethodsWithoutWeatherData =
+ Methods .\\ ("getWeatherData" .== (Location -> Day -> IO WeatherData))
+
+test3 :: Rec MethodsWithoutWeatherData -> Rec WindAndTemperatureMethods
+test3 = id
+
+-- The minimum join of the two rows.
+type JoinedMethods = Methods .\/ Methods
+
+test4 :: Rec JoinedMethods -> Rec Methods
+test4 = id
+```
+
+That's similar to my previous approach with vinyl library — `Methods` is just a union of other methods with its own `getWeatherData` extension. The difference is that vinyl record supports only appending new elements while with rows it's also possible to remove items, take a difference, do a minimum join, override rows, etc.
+
+By using row types as signatures we get more flexible interfaces, more ways to express and combine them. But I don't remember if I ever had a motivation for this in a real world.
\ No newline at end of file