Row types and modules ===================== April 01, 2023 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. 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.