
Datatype Generic Programming in F# Ernesto Rodriguez Wouter Swierstra Utrecht University Utrecht University [email protected] [email protected] Abstract call generic functions on custom datatypes, without having Datatype generic programming enables programmers to de- to implement the underlying conversions manually. fine functions by induction over the structure of types on Yet this approach has not been adopted in other lan- which these functions operate. This paper presents a library guages. In this paper, we will attempt to address this by im- for datatype generic programming in F#, built on top of the plementing a library for data type generic programming in .NET reflection mechanism. The generic functions defined F# (Syme et al. 2012b). More specifically, we make the fol- using this library can be called by any other language run- lowing contributions: ning on the .NET platform. • Categories and Subject Descriptors D.1.1 [Applicative The type system of F# may not be as rich as that of (Functional) Programming]; D.3.3 [Language constructs Haskell, but there are numerous features, including re- and features] flection, subtyping, type providers, and active patterns that may be used for type-level programming in F#. We Keywords datatype generic programming, reflection, F# will briefly present the F# features our library relies upon before describing its implementation (Section 2). 1. Introduction • The core of our library is a representation type defined Over the last decade, datatype generic programming has using the sums-of-products adopted by systems such as emerged as a powerful mechanism for exploiting type struc- Generic Haskell (Hinze and Jeuring 2003) and Regu- ture to define families of functions. In Haskell alone, there lar (Noort et al. 2008). We show how such a represen- are numerous tools and libraries for datatype generic pro- tation type may be implemented in F# and how the con- gramming, including PolyP (Jansson and Jeuring 1997), version functions relating the values of a datatype to its Generic Haskell (Hinze and Jeuring 2003), Scrap your boil- generic representation may be generated automatically erplate (Lammel¨ and Peyton Jones 2003), RepLib (Weirich (Section 3). 2006), Uniplate (Mitchell and Runciman 2007), Regu- • Next, we show how generic functions may be defined lar (Noort et al. 2008), Multi-Rec (Yakushev et al. 2009), over this representation type (Section 4). As an example, Instant Generics (Magalhaes˜ and Jeuring 2011) and many we will implement a generic map function. Instead of re- others. cursing over the representation type directly, we develop Many of these libraries are implemented in the same fash- several auxiliary functions to hide the usage of .NET re- ion. They define a representation type or universe that can be flection and facilitate the definition of generic functions. Generic used to describe some collection of types. functions • Where many Haskell libraries use type classes to imple- are then defined by induction on the elements of the repre- ment type-based dispatch, F#’s overloading mechanism sentation type. Finally, these libraries typically support some is too limited for our purposes. To address this, we will form of conversion between values of algebraic datatypes implement our own system of ad-hoc polymorphism us- and their corresponding representation. This enables users to ing .NET’s reflection. (Section 5) • Finally, we will show how functions from other Haskell Permission to make digital or hard copies of all or part of this work for personal or libraries, such as Uniplate, may be readily implemented classroom use is granted without fee provided that copies are not made or distributed using the resulting library (Section 6). for profit or commercial advantage and that copies bear this notice and the full citation on the first page. Copyrights for components of this work owned by others than ACM must be honored. Abstracting with credit is permitted. To copy otherwise, or republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a We hope that porting the ideas from the datatype generic fee. Request permissions from [email protected]. programming community to F# will pave the way for the WGP ’15, August 30, 2015, Vancouver, British Columbia, Canada. Copyright c 2015 ACM 978-1-nnnn-nnnn-n/yy/mm. $15.00. wider adoption of these ideas in other .NET languages, such http://dx.doi.org/10.1145/nnnnnnn.nnnnnnn as C#. 2. Background This example demonstrates the different type declarations This section introduces the F# language and the .NET plat- that F# supports. Besides records, such as Metadata, F# form. Inspired by the ‘Scrap your boilerplate’ approach to supports algebraic datatypes (ADTs) that should be famil- datatype generic programming (Lammel¨ and Peyton Jones iar to functional programmers. For example, Company, 2003), we will define a company type and a function called Department and Staff are ADTs. Furthermore, program- IncreaseSalary. The function increases the salary of every mers in F# may define classes, such as Employee and employee in the company. Our example is different since our GuatemalanEmployee. There are several important differ- library doesn’t support mutually recursive data types and we ences between classes and datatypes. Records and datatypes will use this example to present the different type declara- may be deconstructed through pattern matching and are im- tions allowed in F#. We will provide an alternative definition mutable. In contrast to classes, there is no possible subtyping of IncreaseSalary using our library for generic program- relation between datatypes. In .NET terminology, they are ming in the second half of this paper. sealed. Classes in F#, on the other hand, behave just as in any other object oriented language. They can inherit from other 2.1 The F# language classes – in our example the class GuatemalanEmployee The F# (Syme et al. 2012b) programming language is a func- inherits from the Employee class. Both ADTs and classes tional language of the ML family. It aims to combine the ad- may contain member functions (or methods) declared with vantages of functional programming and Microsoft’s .NET the member keyword. Member functions always take the platform. To do so, the F# designers have adopted numerous object on which they are invoked as an argument, as wit- features from languages such as Haskell or OCaml, without nessed by the self identifier before a member function’s sacrificing the ability to interface well with other .NET lan- definition. guages. As a result, the language has a much simpler type These data declarations also use polymorphic types and system than the type system of Haskell, OCaml or Scala. On type constraints. In our example, Company, Department the other hand, F# performs no type erasure when compiled and Staff are parametrized by a single type argument. These to the .NET platform. type arguments have a type constraint, stating that they must Before we can define the IncreaseSalary function, we be a subtype of the Employee class. The type constraints are will define the types on which it operates: declared using the when keyword. It is worth pointing out that type arguments can only [hAbstractClassi] be of kind ∗. This is a particularly important limitation in type Employee () = class the context of datatype generic programming since many abstract Salary :float with get and set Haskell libraries rely on higher kinds. abstract NetSalary :float with get Next, we implement the IncreaseSalary function. To do end so, we will begin by defining an auxiliary function called type Metadata = UpdateEmployee that applies its argument function to every fname :string; country :string g Employee of the company: type Staff h`t when `t ≺ Employeei = j Empty j Member of `t ∗ Staff h`ti type Staff h`ti with type Departmenth`t when `t ≺ Employeei = member self :UpdateEmployee (f ) = j Tech of Metadata ∗ Staff h`ti match self : with j HR of Metadata ∗ Staff h`ti j Empty ! Empty type Companyh`t when `t ≺ Employeei = j Member (m; s) ! j Empty Member (f m; s:UpdateEmployee f ) j Dept of Departmenth`ti ∗ Companyh`ti type Departmenth`ti with type GuatemalanEmployee (salary0 :int) = member self :UpdateEmployee (f ) = class match self : with inherit Employee () j Tech of meta; staff ! let mutable salary = salary0 Tech (meta; staff :UpdateEmployee f ) override self :Salary j HR of meta; staff ! with get () = salary HR (meta; staff :UpdateEmployee f ) and set (value) = salary value override self :NetSalary with get () = self :Salary = 1:12 end type Companyh`ti with type. The F# language uses the keyword inherit to denote member self :UpdateEmployee (f ) = that a type inherits from another type. The subtyping relation match self : with does not extend automatically to parametrized types: that is, j Empty ! Empty the implication τa ≺ τb ) Thτai ≺ Thτbi does not hold j Member d; c ! in general. Member ( Reflection To handle all type operations and collect type d:UpdateEmployee f ; information, the .NET platform defines the abstract class c:UpdateEmployee f ) Type. Instances of the class Type encode all the type infor- mation about values. When F# is compiled to the .NET inter- Here we have chosen to overload the UpdateEmployee mediate language, CIL, an instance of the Type class is gen- function, allowing a function with the same name to be de- erated for every type defined in the F# code. The .NET plat- fined for different types. To overload functions in F#, they form makes this type information available at runtime. Every must be defined as a member function. Member functions object has the method GetType which returns the value of may be defined post-hoc, i.e., after the type has been de- type Type. fined. The Type class is not sealed. Languages can extend it Using UpdateEmployee, the IncreaseSalary function with any information they want.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages12 Page
-
File Size-