Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf ·...

13
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 Total Haskell is Reasonable Coq Antal Spector-Zabusky Joachim Breitner Christine Rizkallah Stephanie Weirich {antals,joachim,criz,sweirich}@cis.upenn.edu University of Pennsylvania Philadelphia, PA, USA Abstract We would like to use the Coq proof assistant to mechan- ically verify properties of Haskell programs. To that end, we present a tool, named hs-to-coq, that translates total Haskell programs into Coq programs via a shallow embed- ding. We apply our tool in three case studies – a lawful Monad instance, “Hutton’s razor”, and an existing data structure li- brary – and prove their correctness. These examples show that this approach is viable: both that hs-to-coq applies to existing Haskell code, and that the output it produces is amenable to verification. 1 Introduction The Haskell programming language is a great tool for produc- ing pure, functional programs. Its type system tracks the use of impure features, such as mutation and IO, and its standard library promotes the use of mathematically-inspired struc- tures that have strong algebraic properties. At the same time, Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell Compiler, GHC) [21], and supported by mature software development tools, such as IDEs and testing environments. However, Haskell programmers typically reason about their code only informally. Most proofs are done on paper, by hand, which is tedious, error-prone, and does not scale. On the other hand, the Coq proof assistant [22] is a great tool for writing proofs. It allows programmers to reason about total functional programs conveniently, efficiently, and with high confidence. However, Coq lacks GHC’s extensive ecosystem for program development. Therefore, we propose a multimodal approach to the veri- fication of total functional programs: write code in Haskell and prove it correct in Coq. To support this plan, we have developed an automatic translator, called hs-to-coq, that allows this approach to scale. Permission to make digital or hard copies of part or all of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. Copyrights for third- party components of this work must be honored. For all other uses, contact the owner/author(s). CPP’18, January 2018, Los Angeles, CA, USA © 2018 Copyright held by the owner/author(s). ACM ISBN 978-x-xxxx-xxxx-x/YY/MM. hps://doi.org/10.1145/nnnnnnn.nnnnnnn For example, consider the standard map function on lists (from the Haskell Prelude), and the corresponding Functor instance. map :: (a -> b) -> [ a ] -> [ b ] map f [] = [] map f (x : xs) = fx : map f xs instance Functor [] where fmap = map Our tool translates this Haskell program automatically to the analogous Coq definitions. The map function becomes the expected fixpoint. Definition map {a} {b} : (a -> b) -> list a -> list b := fix map arg_62__ arg_63__ := match arg_62__, arg_63__ with | , nil => nil | f, cons x xs => cons (fx)(map f xs) end . Similarly, the Functor type class in Haskell turns into a Coq type class of the same name, and Haskell’s Functor instance for lists becomes a type class instance on the Coq side. Once the Haskell definitions have been translated to Coq, users can prove theorems about them. For example, we pro- vide a type class for lawful functors: Class FunctorLaws (t : Type -> Type) `{Functor t} := {functor_identity : forall a (x : ta), fmap id x = x; functor_composition : forall abc (f : a -> b)(g : b -> c)(x : ta), fmap g (fmap f x) = fmap (g f) x} . A list instance of the FunctorLaws type class is a formal proof that the list type, using this definition of map, is a lawful functor. This process makes sense only for inductive data types and total, terminating functions. This is where the semantics of lazy and strict evaluation, and hence of Haskell and Coq, coincide [9]. However, the payoff is that a successful transla- tion is itself a termination proof, even before other properties have been shown. Furthermore, because Coq programs may be evaluated (within Coq) or compiled (via extraction) these properties apply, not to a formal model of computation, but to actual runnable code. Our overarching goal is to make it easy for a Haskell pro- grammer to produce Coq versions of their programs that are suitable for verification. The Coq rendition should closely follow the Haskell code – the same names should be used, 1

Transcript of Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf ·...

Page 1: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

Total Haskell is Reasonable CoqAntal Spector-Zabusky Joachim Breitner Christine Rizkallah Stephanie Weirich

{antals,joachim,criz,sweirich}@cis.upenn.eduUniversity of Pennsylvania

Philadelphia, PA, USA

AbstractWe would like to use the Coq proof assistant to mechan-ically verify properties of Haskell programs. To that end,we present a tool, named hs-to-coq, that translates totalHaskell programs into Coq programs via a shallow embed-ding.We apply our tool in three case studies – a lawful Monadinstance, “Hutton’s razor”, and an existing data structure li-brary – and prove their correctness. These examples showthat this approach is viable: both that hs-to-coq appliesto existing Haskell code, and that the output it produces isamenable to verification.

1 IntroductionTheHaskell programming language is a great tool for produc-ing pure, functional programs. Its type system tracks the useof impure features, such as mutation and IO, and its standardlibrary promotes the use of mathematically-inspired struc-tures that have strong algebraic properties. At the same time,Haskell development is backed by an industrial-strengthcompiler (the Glasgow Haskell Compiler, GHC) [21], andsupported by mature software development tools, such asIDEs and testing environments.However, Haskell programmers typically reason about

their code only informally. Most proofs are done on paper,by hand, which is tedious, error-prone, and does not scale.

On the other hand, the Coq proof assistant [22] is a greattool for writing proofs. It allows programmers to reasonabout total functional programs conveniently, efficiently, andwith high confidence. However, Coq lacks GHC’s extensiveecosystem for program development.

Therefore, we propose a multimodal approach to the veri-fication of total functional programs: write code in Haskelland prove it correct in Coq. To support this plan, we havedeveloped an automatic translator, called hs-to-coq, thatallows this approach to scale.

Permission to make digital or hard copies of part or all of this work forpersonal or classroom use is granted without fee provided that copies arenot made or distributed for profit or commercial advantage and that copiesbear this notice and the full citation on the first page. Copyrights for third-party components of this work must be honored. For all other uses, contactthe owner/author(s).CPP’18, January 2018, Los Angeles, CA, USA© 2018 Copyright held by the owner/author(s).ACM ISBN 978-x-xxxx-xxxx-x/YY/MM.https://doi.org/10.1145/nnnnnnn.nnnnnnn

For example, consider the standard map function on lists(from the Haskell Prelude), and the corresponding Functorinstance.

map :: (a -> b) -> [a] -> [b]map f [ ] = [ ]

map f (x : xs) = f x : map f xs

instance Functor [ ] where fmap = map

Our tool translates this Haskell program automatically tothe analogous Coq definitions. The map function becomesthe expected fixpoint.

Definition map {a} {b} : (a -> b) -> list a -> list b :=fix map arg_62__ arg_63__

:= match arg_62__, arg_63__ with| , nil => nil| f, cons x xs => cons (f x) (map f xs)end .

Similarly, the Functor type class in Haskell turns into a Coqtype class of the same name, and Haskell’s Functor instancefor lists becomes a type class instance on the Coq side.

Once the Haskell definitions have been translated to Coq,users can prove theorems about them. For example, we pro-vide a type class for lawful functors:

Class FunctorLaws (t : Type -> Type) `{Functor t} :={functor_identity :

forall a (x : t a), fmap id x = x;functor_composition :

forall a b c (f : a -> b) (g : b -> c) (x : t a),fmap g (fmap f x) = fmap (g ◦ f) x} .

A list instance of the FunctorLaws type class is a formalproof that the list type, using this definition of map, is alawful functor.This process makes sense only for inductive data types

and total, terminating functions. This is where the semanticsof lazy and strict evaluation, and hence of Haskell and Coq,coincide [9]. However, the payoff is that a successful transla-tion is itself a termination proof, even before other propertieshave been shown. Furthermore, because Coq programs maybe evaluated (within Coq) or compiled (via extraction) theseproperties apply, not to a formal model of computation, butto actual runnable code.

Our overarching goal is to make it easy for a Haskell pro-grammer to produce Coq versions of their programs that aresuitable for verification. The Coq rendition should closelyfollow the Haskell code – the same names should be used,

1

Page 2: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

CPP’18, January 2018, Los Angeles, CA, USA Spector-Zabusky, Breitner, Rizkallah, and Weirich

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

even within functions; types should be unaltered; abstrac-tions like type classes and modules should be preserved – sothat the programmer obtains not just a black-box that hap-pens to do the same thing as the original Haskell program,but a live Coq version of the input.Furthermore, the development environment should in-

clude as much as possible of total Haskell. In particular, pro-grammers should have access to standard libraries and lan-guage features and face few limitations other than totality.Also, because programs often change, the generated Coqmust be usable directly, or with declarative modifications, sothat the proofs can evolve with the program.Conversely, an additional application of hs-to-coq is as

a Haskell “rapid prototyping front-end” for Coq. A poten-tial workflow is: (1) implement a program in Haskell first,in order to quickly develop and test it; (2) use hs-to-coqto translate it to the Coq world; and (3) extend and verifythe Coq output. This framework allows diverse groups offunctional programmers and proof engineers to collaborate;focusing on their areas of expertise.Therefore, in this paper, we describe the design and im-

plementation of the hs-to-coq tool and our experienceswith its application in several domains. In particular, thecontributions of this paper are as follows.• We describe the use of our methodology and tool inthree different examples, showing how it can be usedto state and prove the monad laws, replicate textbookequational reasoning, and verify data structure invari-ants (Section 2).• We identify several design considerations in the devel-opment of the hs-to-coq tool itself and discuss ourapproach to resolving the differences between Haskelland Coq (Section 3).• We discuss a Coq translation of the Haskell base li-brary for working with translated programs that wehave developed using hs-to-coq (Section 4).

We discuss related work in Section 5 and future directions inSection 6. Our tool, base libraries, and the case studies arefreely available as open source software.1

2 Reasoning about Haskell code in CoqWe present and evaluate our approach to verifying Haskellin three examples, all involving pre-existing Haskell code.

2.1 Algebraic lawsObjective The Functor type class is not the only class withlaws. Many Haskell programs feature structures that are notonly instances of the Functor class, but also of Applicativeand Monad as well. All three of these classes come with laws.Library authors are expected to establish that their instancesof these classes are lawful (respect the laws). Programmers

1https://github.com/antalsz/hs-to-coq

ContentsFunctor and monad classesFunctions

Naming conventionsBasic Monad functionsGeneralisations of list functionsConditional execution of monadicexpressionsMonadic lifting operatorsStrict monadic functions

Source#

Source#

Functor [] #

Functor Maybe #

Functor IO #

Functor V1 #

Functor U1 #

Functor Par1 #

Functor ReadP #

Functor ReadPrec #

Functor Last #

Functor First #

Functor Product #

Functor Sum #

Functor Dual #

Functor STM #

Functor Handler #

Functor ZipList #

Functor ArgDescr #

Functor OptDescr #

Functor ArgOrder #

Functor Complex #

Functor NonEmpty #

Functor Option #

Functor Last #

Functor First #

Functor Max #

Functor Min #

Functor Identity #

Functor ((->) r) #

Functor (Either a) #

Functor f => Functor (Rec1 f) #

Functor (URec Char) #

Functor (URec Double) #

Functor (URec Float) #

Functor (URec Int) #

Functor (URec Word) #

Functor (URec (Ptr ())) #

Functor ((,) a) #

Functor (ST s) #

Functor (Proxy *) #

Arrow a => Functor (ArrowMonad a) #

Monad m => Functor (WrappedMonad m) #

Functor (ST s) #

Functor (Arg a) #

Functor (K1 i c) #

(Functor g, Functor f) => Functor ((:+:) f g) #

(Functor g, Functor f) => Functor ((:*:) f g) #

(Functor g, Functor f) => Functor ((:.:) f g) #

Functor f => Functor (Alt * f) #

Functor (Const * m) #

Arrow a => Functor (WrappedArrow a b) #

Functor f => Functor (M1 i c f) #

(Functor f, Functor g) => Functor (Product * f g)#

(Functor f, Functor g) => Functor (Sum * f g) #

(Functor f, Functor g) => Functor (Compose * * f g)#

Source#

Control.MonadThe Functor,Monad andMonadPlusclasses, withsome usefuloperationson monads.

Functorandmonadclasses

class Functor f where

The Functor class is used for types that can be mappedover. Instances of Functor should satisfy the followinglaws:

fmap id == idfmap (f . g) == fmap f . fmap g

The instances of Functor for lists, Maybe and IO satisfythese laws.

Minimal complete definition

fmap

Methods

fmap :: (a -> b) -> f a -> f b

Instances

class Applicative m => Monad m where

The Monad class defines the basic operations over a monad,a concept from a branch of mathematics known ascategory theory. From the perspective of a Haskellprogrammer, however, it is best to think of a monad as anabstract datatype of actions. Haskell's do expressionsprovide a convenient syntax for writing monadicexpressions.

Instances of Monad should satisfy the following laws:

return a >>= k = k a

m >>= return = m

m >>= (\x -> k x >>= h) = (m >>= k) >>= h

Furthermore, the Monad and Applicative operationsshould relate as follows:

pure = return

(<*>) = ap

The above laws imply:

fmap f xs = xs >>= return . f

(>>) = (*>)

Source Contents Indexbase-4.9.1.0: Basic libraries

Copyright (c) The University ofGlasgow 2001

LicenseBSD-style (see thefile libraries/base/LICENSE)

Maintainer [email protected] provisionalPortability portableSafeHaskell

Trustworthy

Language Haskell2010

1 von 4

Figure 1. The documentation of Monad lists the three monadlaws and the two laws relating it to Applicative.

using their libraries may then use these laws to reason abouttheir code.

For example, the documentation for the Monad type class,shown in Figure 1, lists the three standard Monad laws as wellas two more laws that connect the Monad methods to thoseof its superclass Applicative. Typically, reasoning aboutthese laws is done on paper, but our tool makes mechanicalverification available.

In this first example, we take the open source successorslibrary [3] and show that its instances of the Functor,Applicative, and Monad classes are lawful. This library pro-vides a type Succs that represents one step in a nondetermin-istic reduction relation; the type class instances allow us tocombine two relations into one that takes a single step fromeither of the original relations. Figure 2 shows the complete,unmodified code of the library. The source code also con-tains, as a comment, 80 lines of manual equational reasoningestablishing the type class laws.

Experience Figure 3 shows the generated Coq code forthe type Succs and the Monad instance. The first line is thecorresponding definition of the Succs data type. Becausethe Haskell program uses the same name for both the typeconstructor Succs and its single data constructor, hs-to-coqautomatically renames the latter to Mk_Succs to avoid thisname conflict.The rest of the figure contains the instance of the Monad

type class for the Succs type. This code imports a Coq ver-sion of Haskell’s standard library base that we have alsodeveloped using hs-to-coq (see Section 4). The Monad type

2

Page 3: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

Total Haskell is Reasonable Coq CPP’18, January 2018, Los Angeles, CA, USA

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

module Control.Applicative.Successors where

data Succs a = Succs a [a] deriving (Show, Eq)

getCurrent :: Succs t -> tgetCurrent (Succs x ) = x

getSuccs :: Succs t -> [t]getSuccs (Succs xs) = xs

instance Functor Succs wherefmap f (Succs x xs) = Succs (f x) (map f xs)

instance Applicative Succs wherepure x = Succs x [ ]Succs f fs <*> Succs x xs= Succs (f x) (map ($x) fs ++ map f xs)

instance Monad Succs whereSuccs x xs >>= f= Succs y (map (getCurrent . f) xs ++ ys)where Succs y ys = f x

Figure 2. The successors library

class from that library, shown below, is a direct translationof GHC’s implementation of the base libraries.

Class Monad m `{Applicative m} := {op_zgzg__ : forall {a} {b}, m a -> m b -> m b;op_zgzgze__ : forall {a} {b}, m a -> (a -> m b) -> m b;return_ : forall {a}, a -> m a} .

Infix " >> " := (op_zgzg__) (at level 99) .Notation "'_ >> _'" := (op_zgzg__) .Infix " >>= " := (op_zgzgze__) (at level 99) .Notation "'_ >>= _'" := (op_zgzgze__) .

As in Haskell, the Monad class includes the return and>>= methods, which form the mathematical definition ofa monad, as well as an additional sequencing method >>.Again due to restrictions on naming, the Coq version usesalternative names for all three of these methods. As returnis a keyword, the tool replaces it by return_. Furthermore,Coq does not support variables with symbolic names, sothe bind and sequencing operators are replaced by namesstarting with op_ (such as op_zgzgze__, the translation of>>=). These names are systematically derived using GHC’s“Z-encoding”.

Note that our version of the Monad type class does notinclude the infamous method fail::Monad m=>String->m a.For many monads, including Succs, a function with thistype signature is impossible to implement in Coq – thismethod is frequently partial.2 As a result, we have instructed

2In fact, this is considered to be a problem in Haskell as well, so the methodis currently being moved into its own class, MonadFail; we translate thisclass (in the module Control.Monad.Fail) as well, for monads that havetotal definitions of this operation.

Inductive Succs a : Type :=Mk_Succs : a -> list a -> Succs a .

(∗ Instances for Functor and Applicative omitted. ∗)

Local Definition instance_Monad_Succs_op_zgzgze__: forall {a} {b}, Succs a -> (a -> Succs b) -> Succs b:= fun {a} {b} => fun arg_4__ arg_5__ =>

match arg_4__, arg_5__ with| Mk_Succs x xs, f => match f x with| Mk_Succs y ys => Mk_Succs y(app (map (compose getCurrent f) xs) ys)

endend .

Local Definition instance_Monad_Succs_return_: forall {a}, a -> Succs a := fun {a} => pure .

Local Definition instance_Monad_Succs_op_zgzg__: forall {a} {b}, Succs a -> Succs b -> Succs b:= fun {a} {b} => op_ztzg__ .

Instance instance_Monad_Succs : Monad Succs := {op_zgzg__ := fun {a} {b} =>instance_Monad_Succs_op_zgzg__;

op_zgzgze__ := fun {a} {b} =>instance_Monad_Succs_op_zgzgze__;

return_ := fun {a} =>instance_Monad_Succs_return_} .

Figure 3. Excerpt of the Coq code produced from Figure 2.(To fit the available width, module prefixes are omitted andlines are manually re-wrapped.)

hs-to-coq to skip this method when translating the Monadclass and its instances.

The instance of the Monad class in Figure 3 includes defini-tions for all three members of the class. The first definition istranslated from the >>= method of the input file; hs-to-coqsupplies the other two components from the default defini-tions in the Monad class.Our base library also includes an additional type class

formalizing the laws for the Monad class, shown in Fig-ure 4. These laws directly correspond to the documenta-tion in Figure 1. Using this definition (and similar ones forFunctorLaws and ApplicativeLaws), we can show that theCoq implementation satisfies the requirements of this class.These proofs are straightforward and are analogous to thereasoning found in the handwritten 80 line comment in thelibrary.

Conclusion The proofs about Succs demonstrate that wecan translate Haskell code that uses type classes and in-stances using Coq’s support for type classes. We can thenuse Coq to perform reasoning that was previously donemanually, and we can support this further by capturing therequirements of type classes in additional type classes.

3

Page 4: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

CPP’18, January 2018, Los Angeles, CA, USA Spector-Zabusky, Breitner, Rizkallah, and Weirich

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

Class MonadLaws (t : Type -> Type)`{!Functor t, !Applicative t, !Monad t,

!FunctorLaws t, !ApplicativeLaws t} :={monad_left_id : forall A B (a : A) (k : A -> t B),(return_ a >>= k) = (k a);

monad_right_id : forall A (m : t A),(m >>= return_) = m;

monad_composition : forall A B C(m : t A) (k : A -> t B) (h : B -> t C),(m >>= (fun x => k x >>= h)) = ((m >>= k) >>= h);

monad_applicative_pure : forall A (x : A),pure x = return_ x;

monad_applicative_ap : forall A B(f : t (A -> B)) (x : t A),(f <*> x) = ap f x} .

Figure 4. Coq type class capturing the Monad laws.

2.2 Hutton’s razorObjective Our next case study is “Hutton’s razor”, fromProgramming in Haskell [17]. It includes a small expressionlanguage with an interpreter and a simple compiler from thislanguage to a stack machine [17, Section 16.7]. We presentour version of his code in Figure 5.

Hutton uses this example to demonstrate how equationalreasoning can be used to show compiler correctness. In otherwords, Hutton shows that executing the output of the com-piler with an empty stack produces the same result as evalu-ating an expression:

exec (comp e) [ ] = Just [eval e]

Experience Even in this simple example, the design of thecompiler and its correctness proof are subtle. In particular, inHutton’s original presentation, the exec function is partial:it does not handle stack underflow. This partiality guidesHutton’s design; he presents and rejects an initial version ofthe comp function because of this partiality.

Since Coq does not support partial functions, this posed animmediate problem. This is why the code in Figure 5 has beenmodified: we changed exec to return a Maybe Stack, notsimply a Stack, and added the final equation. Once we madethis small change and translated the code with hs-to-coq,the proof of compiler correctness was easy. In fact, in Coq’sinteractive mode, users can follow the exact same (small)steps of reasoning for this proof that Hutton provides in histextbook – or use Coq’s proof automation to significantlyspeed up the proof process.

Conclusion We were successfully able to replicate a text-book correctness proof for a Haskell programs, but along theway, we encountered the first significant difference betweenCoq and Haskell, namely partiality (Section 3.7 providesmore details). Since we only set out to translate total code,

module Compiler where

data Expr = Val Int | Add Expr Expr

eval :: Expr -> Inteval (Val n) = neval (Add x y) = eval x + eval y

type Stack = [Int]

type Code = [Op]

data Op = PUSH Int | ADD

exec :: Code -> Stack -> Maybe Stackexec [ ] s = Just sexec (PUSH n : c) s = exec c (n : s)exec (ADD : c) (m : n : s) = exec c (n + m : s)exec (ADD : c) = Nothing

comp :: Expr -> Codecomp e = comp' e [ ]

comp' :: Expr -> Code -> Codecomp' (Val n) c = PUSH n : ccomp' (Add x y) c = comp' x (comp' y (ADD : c))

Figure 5. Hutton’s razor

we needed to update the source code to be total; once we didso, we could translate the textbook proofs to Coq directly.

2.3 Data structure correctnessObjective In the last case study, we apply our tool to self-contained code that lives within a large, existing code base.The Bag module3 from GHC [21] implements multisets withthe following data type declaration.

data Bag a= EmptyBag| UnitBag a| TwoBags (Bag a) (Bag a)

–– INVARIANT: neither branch is empty| ListBag [a]

–– INVARIANT: the list is non-empty

The comments in this declaration specify the two invariantsthat a value of this type must satisfy. Furthermore, at the topof the file, the documentation gives the intended semantics ofthis type: a Bag is “an unordered collection with duplicates”.In fact, the current implementation satisfies the strongerproperty that all operations on Bags preserve the order ofelements, so we can say that their semantics is given by thefunction bagToList :: Bag a -> [a], which is defined in themodule.

Experience The part of the module that we are interestedin is fairly straightforward; in addition to the Bag type, itcontains a number of basic functions, such as3 http://git.haskell.org/ghc.git/blob/ghc-8.0.2-release:/compiler/utils/Bag.hs

4

Page 5: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

Total Haskell is Reasonable Coq CPP’18, January 2018, Los Angeles, CA, USA

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

isEmptyBag :: Bag a -> BoolunionBags :: Bag a -> Bag a -> Bag a

We formalize the combined invariants as a boolean predi-cate well_formed_bag. Then, for each translated function,4we prove up to two theorems:

1. We prove that each function is equivalent, with respectto bagToList, to the corresponding list function.

2. If the function returns a Bag, we prove that it preservesthe Bag invariants.

Thus, for example, we prove the following three theoremsabout isEmptyBag and unionBags:

Theorem isEmptyBag_ok {A} (b : Bag A) :well_formed_bag b ->isEmptyBag b = null (bagToList b) .

Theorem unionBags_ok {A} (b1 b2 : Bag A) :bagToList (unionBags b1 b2) =bagToList b1 ++ bagToList b2 .

Theorem unionBags_wf {A} (b1 b2 : Bag A) :well_formed_bag b1 -> well_formed_bag b2 ->well_formed_bag (unionBags b1 b2) .

Interestingly, we can see that isEmptyBag’s correctness the-orem requires that its argument satisfy the Bag invariants,but unionBags’s does not.

Verifying Bag The verification effort proceeded just asthough we were verifying any data structure library writtenin Coq. We verified nineteen different functions on Bags, andno proof was longer than eight lines (using the ssreflecttactic library [13]).Along the way, we discovered a minor omission in the

documentation of the foldBag function. This function hastype

foldBag :: (r -> r -> r) -> (a -> r) -> r -> Bag a -> r

The expression foldBag t u e maps u over every elementof the bag and then, starting with e, combines these resultsfrom the right using the operator t, à la foldr.The documentation for foldBag requires that t be asso-

ciative, and says that it is then a “more tail-recursive” ver-sion of a commented-out reference implementation whichcombines the results according the internal structure of theBag instead of from the right. However, as we discoveredwhen attempting to prove the two implementations equal,the reference implementation is not the same as foldBagin all cases – they are only the same when e is the identityfor t. This discrepancy is minor, but has been present forover 21 years [25].

4We skipped monadic functions such as mapBagM, along with three furtherfunctions that referred to code we did not translate.

Selectively translating Bag As a part of GHC, the Bag mod-ule cannot stand on its own; it imports a number of othermodules fromGHC, such as Outputable and Util. However,there is a great deal of code we don’t care about in GHC. Forexample, the Outputable module contains infrastructurefor pretty printing. For our verification goals, this module iscompletely irrelevant, so it would be unfortunate if we couldnot proceed without translating it into Coq. But it would beequally unfortunate if we had to edit the GHC sources toremove code that we were not interested in.It is for these sorts of reasons that hs-to-coq supports

declaratively configuring the translation process: it can takeas input a file of declarative instructions, called edits, thatinfluence the translation process. One such instruction is toskip translating a module:

skip module Outputable

Similar instructions exist to skip functions, type classes, in-stances and type class methods; for example, the Util mod-ule contains a number of utility functions that aren’t usedby Bag, and so are unnecessary.

Conclusion Because hs-to-coq’s translation is configur-able, we were able to slice the code of interest out of a large,existing codebase, without having to translate irrelevantparts or change the original source code. Once translated,the code was pleasant and straightforward to work with,and we completed both invariant preservation and semanticcorrectness proofs. We also saw that specifications are sub-tle, and edge cases in documentation can be caught by suchverification.

3 The design and implementation ofhs-to-coq

The previous section describes hs-to-coq in action: it pro-cesses a Haskell program, along with a separate files of“edits”, which are commands that modify the translation inwell-defined ways, and produces verifiable Coq code. Ourdesign goals for hs-to-coq include:

1. Produce output resembling the original input;2. Produce output amenable to interactive proof devel-

opment;3. Handle features commonly found in modern Haskell

developments; and4. Apply to source code as is, even if it is part of a larger

development.We have made the somewhat controversial choice to focuson total Haskell programs. This choice follows from our firsttwo goals above: total programs require fewer modificationsto be accepted by Coq (for example, no need use a monadto model partiality) and provide more assurances (if a trans-lation is successful we know that the code is total). At thesame time, reasoning about total functions is simpler than

5

Page 6: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

CPP’18, January 2018, Los Angeles, CA, USA Spector-Zabusky, Breitner, Rizkallah, and Weirich

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

reasoning about partial ones, so we encourage Haskell proofdevelopment by concentrating on this domain.The configurable edits support this design. Example ed-

its include skipping functions that aren’t being verified, orrenaming a translated type or value to its Coq equivalentfor interoperability. By providing this in a separate file, thisper-project changes do not need to be applied to the codeitself, and do not have to be re-done as the code evolves.We use the Glasgow Haskell Compiler (GHC), version

8.0.2, as a library [21]. By using its parser, hs-to-coq canprocess most Haskell code as seen in the wild. In fact, ourtool adopts the first two stages of GHC. First, the sourcecode passes through the parser and an AST is produced. ThisAST then goes through the renamer, which resolves namereferences and ensures that programs are well scoped. Basedon this, the tool generates the Coq output.

Note that hs-to-coq generates the Coq output before thetypechecking and desugaring phases. Going after the desug-aring, and hence translating GHC’s intermediate languageCore, would certainly simplify the translation. But the result-ing code would look too different from the Haskell sourcecode, and go against our first goal.Many of the syntactic constructs found in Haskell have

direct equivalents in Coq: algebraic data types, function def-initions, basic pattern matching, function application, let-bindings, and so on. Translating these constructs is immedi-ate. Other syntactic constructs may not exist in Coq, but arestraightforward to desugar: where clauses become match orlet expressions, do notation and list comprehensions turninto explicit function calls, etc.However, many complex Haskell features do not map so

cleanly onto Coq features. In the following we discuss ourresolution of these challenging translations in the context ofour design goals.

3.1 Module systemHaskell and Coq have wildly different approaches to theirmodule systems, but thankfully they both have one. Thelargest point of commonality is that in both Haskell and Coq,each source file creates a single module, with its name de-termined by the file name and the path thereto. The methodfor handling modules is thus twofold:

• translate each Haskell file into a distinct Coq file; and• always refer to all names fully qualified to avoid anydifferences between the module systems.

In each Coqmodule, we make available (through Require)all modules that are referenced by any identifiers. We dothis instead of translating the Haskell import statementsdirectly because of one of the differences between Haskelland Coq: Haskell allows a module to re-export identifiersthat it imported, but GHC’s frontend only keeps track ofthe original module’s name. So the fully-qualified name we

generate refers to something further back in the module treethat must itself be imported.

3.2 RecordsIn Haskell, data types can be defined as records. For example,the definition of the functions getCurrent and getSuccs inFigure 2 could be omitted if the data type were defined asdata Succs a = Succs {getCurrent :: a

, getSuccs :: [a]}The type is the same, but naming the fields enables some ex-tra features: (1) unordered value creation, (2) named patternmatching, (3) field accessors, and (4) field updates [20]. Inaddition, with GHC extensions, it also enables (5) record wildcards: a pattern or expression of the form Succs { . . } bindseach field to a variable of the same name.Coq features support for single-constructor records that

can do (1–3), although with minor differences; however, itlacks support for (4–5). More importantly, however, Haskellrecords are per-constructor – a sum type can contain fieldsfor each of its constructors. Coq does not support this at all.Consequently, hs-to-coq keeps track of record field namesduring the translation process. Constructors with recordfields are translated as though they had no field names, andthe Coq accessor functions are generated separately. Duringpattern matching or updates – particularly with wild cards –the field names are linked to the appropriate positional field.

3.3 Patterns in function definitionsHaskell function definitions allow the programmer to havepatterns as parameters:uncurry :: (a -> b -> c) -> (a, b) -> cuncurry f (x, y) = f x y

This code is not allowed in Coq; pattern matching is onlyperformed by the match expression. Instead, programmersfirst have to name the parameter, and then perform a separatepattern match:Definition uncurry {a} {b} {c} :(a -> b -> c) -> a ∗ b -> c :=fun arg_10__ arg_11__ =>match arg_10__, arg_11__ with| f, pair x y => f x y

end .This translation extends naturally to functions that are

defined using multiple equations, as seen in the map functionin Section 1.

3.4 Pattern matching with guardsAnother pattern-related challenge is posed by guards, andtranslation tools similar to ours have gotten their semanticswrong (see Section 5).

Guards are side conditions that can be attached to a func-tion equation or a case alternative. If the pattern matches,

6

Page 7: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

Total Haskell is Reasonable Coq CPP’18, January 2018, Los Angeles, CA, USA

716

717

718

719

720

721

722

723

724

725

726

727

728

729

730

731

732

733

734

735

736

737

738

739

740

741

742

743

744

745

746

747

748

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767

768

769

770

but the condition is not satisfied, then the next equationis tried. A typical example is the take function from theHaskell standard library, where take n xs returns the first nelements of xs:

take :: Int -> [a] -> [a]take n | n <= 0 = [ ]take [ ] = [ ]

take n (x : xs) = x : take (n − 1) xs

The patterns in the first equation match any argument; how-ever, the match only succeeds if n<= 0 as well. If n is positive,that equation is skipped, and pattern matching proceeds tothe next two equations.

Guards occur in three variants:

1. A boolean guard is an expression expr of type Bool, aswe saw in take. It succeeds if expr evaluates to True.

2. A pattern guard is of the form pat ← expr. It suc-ceeds if the expression expr matches the pattern pat,and brings the variables in pat into scope just as anypattern match would.

3. A local declaration of the form let x = e. This binds xto e, bringing x into scope, and always succeeds.

Each equation can be guarded by a multiple guards, sepa-rated by commas, all of which must succeed in turn for thisequation to be used.

Coq does not support guards, so hs-to-coq’s translationhas to eliminate them. Conveniently, the Haskell Report [20]defines the semantics of pattern matching with guards interms of a sequence of rewrites, at the end of which allguards have been removed and all case expressions are ofthe following, primitive form:

case e of K x1 . . . xN -> e1-> e2

According to these rules, the take function defined abovewould be translated to something like

take :: Int -> [a] -> [a]take n xs = if n <= 0

then [ ]else case xs of

[ ] -> [ ]-> case xs of

x : xs -> x : take (n − 1) xs-> error "No match"

Unfortunately, this approach is unsuitable for hs-to-coqas the final pattern match in this sequence requires an catch-all case to be complete. This requires an expression of arbi-trary type, which exists in Haskell (error . . . ), but cannotexist in Coq. Additionally, since Coq supports nested pat-terns (such as Just (x :xs)), we want to preserve them whentranslating Haskell code.

Therefore, we are more careful when translating case ex-pressions with guards, and we keep mutually exclusive pat-terns within the same match. This way, the translated takefunction performs a final pattern match on its list argument:

Definition take {a} : Int -> list a -> list a :=fix take arg_10__ arg_11__

:= let j_13__ :=match arg_10__, arg_11__ with| , nil => nil| n, cons x xs =>cons x (take (op_zm__ n (fromInteger 1)) xs)

end inmatch arg_10__, arg_11__ with| n, => if op_zlze__ n (fromInteger 0)then nilelse j_13__

end .

The basic idea is to combine multiple equations into a singlematch statement, whenever possible. We bind these matchexpressions to a name, here j_13__, that earlier patternsreturn upon pattern failure. We cannot inline this definition,as it would move expressions past the pattern of the sec-ond match expression, which can lead to unwanted variablecapture.

In general, patterns are translated as follows:1. We split the alternatives intomutually exclusive groups.

We consider an alternative a1 to be exclusive with a2if a1 cannot fall through to a2. This is the case ifa. a1 has no guards, orb. an expression matched by the pattern in a1 willnever be matched by the pattern in a2.

2. Each group turns into a single Coq match statementwhich are bound, in reverse order, to a fresh identifier.In this translation, the identifier of the next group isused as the fall-through target.The last of these groups has nothing to fall-throughto. In obviously total Haskell, the fall-through willnot be needed. Partial code uses patternFailure asdiscussed in Section 3.7.If the patterns of the resulting match statement arenot complete, we add a wild pattern case (using ) thatreturns the fall-through target of the current group.

3. Each alternative within such a group turns into onebranch of the match. We translate nested patterns di-rectly, as the semantics of patterns in Coq and Haskellcoincide on the subset supported by hs-to-coq, whichexcludes incomplete irrefutable patterns, view pat-terns, and pattern synonyms [27].At this point, a where clause in the Haskell code (whichspans multiple guards) gets translated to a let thatspans all guarded right-hand-sides.

4. Each guarded right-hand-side of one alternative getsagain bound, in reverse order, to a fresh identifier. The

7

Page 8: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

771

772

773

774

775

776

777

778

779

780

781

782

783

784

785

786

787

788

789

790

791

792

793

794

795

796

797

798

799

800

801

802

803

804

805

806

807

808

809

810

811

812

813

814

815

816

817

818

819

820

821

822

823

824

825

CPP’18, January 2018, Los Angeles, CA, USA Spector-Zabusky, Breitner, Rizkallah, and Weirich

826

827

828

829

830

831

832

833

834

835

836

837

838

839

840

841

842

843

844

845

846

847

848

849

850

851

852

853

854

855

856

857

858

859

860

861

862

863

864

865

866

867

868

869

870

871

872

873

874

875

876

877

878

879

880

last guard uses the fall-through target of the wholemutually exclusive group; the other guards use thenext guard.

5. The sequence of guards of a guarded right-hand-sideare now desugared as follows:a. A boolean guard expr turns into

if expr then . . .else j

b. A pattern guard pat← expr turns into

match expr with | pat => . . .| => j

c. A let guard turns into a let expression scoping overthe remaining guards.

Here, . . . is the translation of the remaining guards or,if none are left, the actual right-hand side expression,and j is the current fall-through target.

This algorithm is not optimal in the sense of producing thefewest match expressions; for example, a more sophisticatednotion of mutual exclusivity could allow an alternative a1even when it has guards, as long as these guards cannot fail(e.g., pattern guards with complete patterns, let-guards).This issue has not yet come up in our test cases.

3.5 Type classes and instancesType classes [33] are one of Haskell’s most prominent fea-tures, and their success has inspired other languages to im-plement this feature, including Coq [29]. As shown in thesuccessors case study (Section 2.1), we use this familial re-lation to translate Haskell type classes into Coq type classes.As can be seen in Figure 3, hs-to-coq lifts the method

definitions out of the Instance. While not strictly requiredthere, this lifting is necessary to allow an instance methodto refers to another method of the same instance.

Superclasses Superclass constraints are turned into ar-guments to the generated class, and these arguments aremarked implicit, so that Coq’s type class resolution mecha-nism finds the right instance. This can be seen in the defini-tion of Class Monad in Section 2.1, where the Applicativesuperclass is an implicit argument to Monad.

Default methods Haskell allows a type class to declaremethods with a default definition. These definitions are in-serted by the compiler into an instance if it omits them. Forexample, the code of the successors library did not give adefinition for Monad’s method return, and so GHC will usethe default definition return = pure.Since Coq does not have this feature, hs-to-coq has to

remember the default method’s definition and include it inthe Instance declarations as needed. This is how themethodinstance_Monad_Succs_return_ in Figure 3 arose.

Derived instances The Haskell standard provides the abil-ity to derive a number of basic type classes (Eq, Ord, . . . ): theHaskell compiler can optionally synthesize whole instancesof these type classes. GHC extends this mechanism to ad-ditional type classes (Functor, Foldable, . . . ). To translatederived instances, we simply take the instance declarationssynthesized by the compiler and translate them just as wedo for user-provided instances.

Self-referential instances Haskell type class instance arein scope even in their own declaration, and idiomatic Haskellcode makes good use of that. Consider the standard instancefor list equality:instance Eq a => Eq [a] where[ ] == [ ] = True(x : xs) == (y : ys) = x == y && xs == ys

== = Falsexs /= ys = not (xs == ys)

The operator == occurs three times on the right hand sideof method definitions, and all three occurrences have to betreated differently:

1. In x == y, which compares list elements, we want touse the polymorphic op_zeze__method, so that Coq’sinstance resolution mechanism picks up the instancefor Eq a.

2. For the first xs == ys, where lists are compared, wecannot use the polymorphic method, because the in-stance Eq [a] is not yet in scope. Instead, we want torefer to the very function that we are defining, so wehave to turn that function into a fixed point.

3. The second xs==ys, in the definition of /=, also cannotbe the polymorphic method. Instead, we want to referto the method function for list’s equality that we havejust defined.

Unfortunately, hs-to-coq does not have the necessary typeinstance resolution information to reliably detect which vari-ant to use. Therefore, we use following heuristic: By default,the polymorphic method is used. But in a method definitionthat is generated based on a default method, the currentlydefined methods are used. When this heuristic fails, the usercan inject the correct definition using redefine edits.

3.6 Order of declarationsIn Haskell, the order of declarations in a source file is irrel-evant; functions, types, type classes, and instances can beused before they are defined. Haskell programmers oftenmake use of this feature. Coq, however, requires declarationsto precede their uses. In order to appease Coq, hs-to-coqdetects the dependencies between the sentences of the Coqfile – a sentence that uses a name depends on it – and usesthis to sort the sentences topologically so that definitionsprecede uses.

8

Page 9: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

881

882

883

884

885

886

887

888

889

890

891

892

893

894

895

896

897

898

899

900

901

902

903

904

905

906

907

908

909

910

911

912

913

914

915

916

917

918

919

920

921

922

923

924

925

926

927

928

929

930

931

932

933

934

935

Total Haskell is Reasonable Coq CPP’18, January 2018, Los Angeles, CA, USA

936

937

938

939

940

941

942

943

944

945

946

947

948

949

950

951

952

953

954

955

956

957

958

959

960

961

962

963

964

965

966

967

968

969

970

971

972

973

974

975

976

977

978

979

980

981

982

983

984

985

986

987

988

989

990

While this works in most cases, due to the desugaring oftype class constraints as invisible implicit arguments (Sec-tion 3.5), this process does not always yield the correct order.In such cases, the user can declare additional dependenciesbetween definitions by adding an order like

order instance_Functor_Dual instance_Monad_Dual

to the edit file.

3.7 Partial HaskellAnother feature5 of Haskell is that it permits partial functionsand general recursion.We have only discussed verifying totalHaskell. Nevertheless, as one starts to work on an existing orevolving Haskell code base, making every function total andobviously terminating should not have to be the first step.Therefore, hs-to-coq takes liberties to produce some-

thing useful, rather than refusing to translate partial func-tions. This way, verification can already start and informfurther development of the Haskell code. When the designstabilizes, the code can be edited for making totality obvious.We can classify translation problems are into four cate-

gories:1. Haskell code with genuinely partial pattern matches;

for example,

head :: [a] -> ahead (x : ) = x

which will crash when passed an empty list.2. Haskell code with pattern matches that look partial,

but are total in away that Coq’s totality checker cannotsee. For example, we can define a run-length encodingfunction in terms of group :: Eq a => [a] -> [[a]]:

runLengthEncoding :: Eq a => [a] -> [(a, Int)]runLengthEncoding =map (\(x : xs) -> (x, 1 + length xs)) . group

Since the group function returns a list of nonemptylists, the partial pattern in the lambda will actuallyalwaysmatch, but this proof is beyond Coq’s automaticreasoning.

3. Haskell code with genuinely infinite recursion, at leastwhen evaluated strictly; for example,

repeat :: a -> [a]repeat x = x : repeat x

produces an infinite list in Haskell, but would divergein Coq using the inductive definition of lists.

4. Haskell code with recursion that looks infinite, butterminates in a way that Coq’s termination checkercannot see. For example, we can implement a sortfunction in terms of the standard functional quicksort-like algorithm:

5Some might prefer quotes around this word.

sort :: Ord a => [a] -> [a]sort [ ] = [ ]sort (p : xs) = sort lesser ++ [p] ++ sort greaterwhere (lesser, greater) = partition (<p) xs

This function recurses on two lists that are alwayssmaller than the argument, but not syntactically, so itwould be rejected by Coq’s termination checker.

Our tool recognizes partial pattern matches, as describedin Section 3.4. If these occur, it adds the axiom

Local Axiom patternFailure : forall {a}, a .

to the output and completes the pattern match with it, e.g.:Definition head {a} : list a -> a :=

fun arg_10__ => match arg_10__ with| cons x => x| => patternFailureend .

Naturally, this axiom is glaringly unsound. But it does allowthe user to continue translating and proving, and to revisitthis issue at a more convenient time – for example, whenthey are confident that the overall structure of their projecthas stabilized. In the case of genuinely partial functions, theuser might want to change their type to be more precise,as we did in Section 2.2. In the case of only superficiallypartial code like runLengthEncoding, small, local changesto the code may avoid the problem. At any time, the usercan use Coq’s Print Assumptions command to check if anyprovisional axioms are left.For non-structural recursion, we follow a similar path.

Since hs-to-coq itself does not perform termination check-ing, it translates all recursive definitions to Coq fixpoints,which must be structurally recursive. If this causes Coqto reject valid code, the user can use an edit of the formnonterminating sort to instruct hs-to-coq to use the fol-lowing axiom to implement the recursion:

Local Axiom unsafeFix : forall {a}, (a -> a) -> a .

Again, this axiom is unsound, but allows the programmerto proceed. In fact, after including the computation axiom

Axiom unroll_unsafeFix : forall a (f : a -> a),unsafeFix f = f (unsafeFix f) .

in the file with the proofs, we were able to verify the correct-ness of the sort function above.Eventually, though, the user will have to address this is-

sue in order to consider their proofs complete. They havemany options: They can apply the common Coq idiom ofadding “fuel”: an additional argument that is structurallydecreasing in each iteration. They can replace the definitionwith one written in Coq, perhaps using the more advancedcommands Function or Program Fixpoint [2], which allowexplicit termination proofs using measures or well-founded

9

Page 10: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

991

992

993

994

995

996

997

998

999

1000

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

1012

1013

1014

1015

1016

1017

1018

1019

1020

1021

1022

1023

1024

1025

1026

1027

1028

1029

1030

1031

1032

1033

1034

1035

1036

1037

1038

1039

1040

1041

1042

1043

1044

1045

CPP’18, January 2018, Los Angeles, CA, USA Spector-Zabusky, Breitner, Rizkallah, and Weirich

1046

1047

1048

1049

1050

1051

1052

1053

1054

1055

1056

1057

1058

1059

1060

1061

1062

1063

1064

1065

1066

1067

1068

1069

1070

1071

1072

1073

1074

1075

1076

1077

1078

1079

1080

1081

1082

1083

1084

1085

1086

1087

1088

1089

1090

1091

1092

1093

1094

1095

1096

1097

1098

1099

1100

relations. Or, of course, they can refactor the code to avoidthe problematic functions at all.

Thus, the intended workflow around partiality and generalrecursion is to begin with axioms in place, which is notan unusual approach to proof development, and eliminatethem at the end as necessary. For example, the correctnesstheorem about Hutton’s razor in Section 2.2 goes througheven before changing the exec function to avoid the partialpattern match! The reason is that the correctness theoremhappens to onlymake a statement about programs and stacksthat do not trigger the pattern match failure.

3.8 Infinite data structuresAs a consequence of Haskell’s lazy evaluation, Haskell datatypes are inherently coinductive. For example, a value oftype [Int] can be an infinite list. This raises the questionof whether we should be making use of Coq’s support forcoinductive constructions, and using CoInductive insteadof Inductive in the translation of Haskell data types. Thetwo solutions have real tradeoffs: with corecursion, wewouldgain the ability to translate corecursive functions such asrepeat (mentioned in Section 3.7) using cofix, but at theprice of our present ability to translate recursive functionssuch as filter and length.We conjecture, based on our experience as Haskell pro-

grammers, that there is a lot of Haskell code that workslargely with finite values. Moreover, many idioms that do useinfinite data structures (e.g., zipWith [0 . . ]) can be rewrit-ten to work only with finite values. And reasoning aboutcoinduction and corecursion is much trickier than reasoningabout induction and recursion, especially in Coq.

3.9 Unsupported language featuresThere are language constructs that hs-to-coq simply doesnot yet support, such as mutually recursive definitions, in-complete irrefutable patterns, and a number of languageextensions. If any of these features is used in a definition,then hs-to-coq creates an axiom with the name and typeof the problematic definition so that it does not hold up thetranslation of code using this function. A code commentnext to the axiom explains the nature of the failure. As weencounter these missing features, we extend hs-to-coq tosupport them.

4 GHC’s base libraryThe case studies in Section 2 build upon a Coq version ofGHC’s base library [8] that we are developing as part of thisproject. This section discusses the design questions raisedby constructing such a library. This process also stress-testshs-to-coq itself.

Primitive types and operationsGHC.Prim, GHC.Tuple, GHC.Num, GHC.Char,GHC.Base

Prelude types and classesGHC.Real, Data.Bool, Data.Tuple, Data.Maybe,Data.Either, Data.Void, Data.Function,GHC.Enum, Data.Ord, Data.Bits

List operationsGHC.List, Data.List, Data.OldList

Algebraic structuresData.Functor, Data.Functor.Const,Control.Monad, Control.Monad.Fail,Data.Monoid, Data.Traversable, Data.Foldable,Control.Category, Control.Arrow

Figure 6. Coq base library modules

4.1 What is in the library?Our base library consists of a number of different modules asshown in Figure 6. Thesemodules include definitions of prim-itive types (Int, Integer, Char, Word) and their primitiveoperations, and common data types ([ ], Bool, Maybe, Either,Void, Ordering, tuples) and their operations from the stan-dard prelude. They also include prelude type classes (Eq, Ord,Enum, Bounded) as well as classes for algebraic structures(Monoid, Functor, Applicative, Monad, Arrow, Category,Foldable, Traversable) and data types that assist withthese instances.

During the development of this library we faced the designdecision of whether we should translate all Haskell code tonew Coq definitions, or whether we should connect Haskelltypes and functions to parts of the Coq standard library. Wehave chosen to do the latter, mapping basic Haskell types(such as Bool, [ ], Either, Maybe, and Ordering) to theirCoq counterparts (respectively bool, list, sum, option, andcomparison). This makes the output slightly less familiar toHaskell programmers – users must know how these typesand constructors match up. However, it also makes existingCoq proofs about these data structures available.Support for this mapping in hs-to-coq is provided via

rename edits, which allow us to make that decision on aper-type and per-function basis, as the following excerpt ofthe edit file shows:

rename type GHC.Types.[] = list

rename value GHC.Types.[] = nil

rename value GHC.Types.: = cons

The library also includes (handwritten) modules that spec-ify and prove properties of this code, including type classesthat describe lawful functors, applicative functors, and mon-ads, as discussed in Section 2.1. We include proofs that thetype constructors list and option are lawful functors, ap-plicative functors, and monads by instantiating these classes.

10

Page 11: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

1101

1102

1103

1104

1105

1106

1107

1108

1109

1110

1111

1112

1113

1114

1115

1116

1117

1118

1119

1120

1121

1122

1123

1124

1125

1126

1127

1128

1129

1130

1131

1132

1133

1134

1135

1136

1137

1138

1139

1140

1141

1142

1143

1144

1145

1146

1147

1148

1149

1150

1151

1152

1153

1154

1155

Total Haskell is Reasonable Coq CPP’18, January 2018, Los Angeles, CA, USA

1156

1157

1158

1159

1160

1161

1162

1163

1164

1165

1166

1167

1168

1169

1170

1171

1172

1173

1174

1175

1176

1177

1178

1179

1180

1181

1182

1183

1184

1185

1186

1187

1188

1189

1190

1191

1192

1193

1194

1195

1196

1197

1198

1199

1200

1201

1202

1203

1204

1205

1206

1207

1208

1209

1210

4.2 How did we develop the library?Because of the nature of base, some modules were moreamenable to automatic translation than others. Of those wetranslated, half were defined via automatic translation fromthe GHC source (with the assistance of edit instructions), andhalf required user assistance (usually through modificationof the output of the translation).

Wewere forced to manually define themodules that defineprimitive types, such as GHC.Word, GHC.Char, and GHC.Num,because they draw heavily on a feature that we do not sup-port: unboxed types. Instead, we translate primitive numerictypes to signed and unsigned binary numbers in Coq (Z andN, respectively). Similarly, we translate Rational to Coq’stype Q of rational numbers. In the case of fixed precisiontypes, we have chosen these mappings for expediency; infuture work, we plan to switch these definitions so that wecan reason about underflow and overflow.Moreover, we were limited in some ways by the expres-

siveness of our tool. As we describe in Section 3.5, we arenot able to translate all type class instances to Coq. Further-more, some modules make heavy use of safe coercions [4],which cannot be expressed in Coq. Finally, limitations in thedifference between Haskell’s type inference and Coq’s typeinference prevented the completely automatic translation ofControl.Category and Data.Functor.Const.

On the other hand, we were able to successfully generateseveral modules in the base library, including the primary fileGHC.Base and the list libraries GHC.List and GHC.OldList.Other notable successes include translating the alge-braic structure libraries Data.Monoid, Data.Foldable,Data.Traversable, and Control.Monad.

4.3 What is skipped?During the translation process, the edits allow us to skipdefinitions found in the Haskell input. Most modules had atleast one skipped definition, and under a quarter had morethan twenty.Many of the skipped definitions are due to partiality. For

example, we do not translate functions that could triggerpattern match failure, such as head or maximum, or that coulddiverge, such as repeat or iterate.Some type classes have methods that are often instanti-

ated with partial functions. We also removed such members,such as the fail method of the Monad class (as mentionedin Section 2.1), the foldl1, foldr1, maximum and minimummethods of the Foldable class, and the enumFromThen andenumFromThenTo methods of the Enum class. In the last case,this is not all of the partial methods of the class; for exam-ple, the pred and succ methods throw errors in instancesfor bounded types, and the enumFrom method diverges forinfinite types. To solve this problem, we have chosen to sup-port the Enum class only for bounded types. In this case, wemodified the pred and succ methods so that they return the

minBound and maxBound elements, respectively, at the endof their ranges. For enumFrom, we use maxBound to providean end point of the enumeration.

Some functions are total, but it is difficult for Coq to deter-mine that they are. For example, the eftInt function in theEnum module enumerates a list of integers from a startingnumber x to an ending number y. This function is not struc-turally recursive, so we use the Program Fixpoint extensionto provide its termination proof in our redefinition.Some parts of these modules are skipped because they

relate to operations that are out of scope for our tool. Wedo not translate any definitions or instances related to IO.We also do not plan to support reflection, so all type classinstances related to GHC.Generics are omitted. Similarly, wedo not include arrays, so we skip instances related to arraytypes and indexing.

5 Related Work5.1 Haskell and CoqExtraction The semantic proximity of Haskell and Coq,which we rely on, is also used in the other direction by Coq’ssupport for code extraction to Haskell [19]. Several projectsuse this feature to verify Haskell code [7, 18]. It cannot beused to verify pre-existing Haskell code though.

Manual translation The coq-haskell library [34] is ahand-written Coq library designed to make it easier forHaskell programmers towork in Coq. Inmanyways, it servesa similar purpose to our translation of base (Section 4). In ad-dition to enabling easier Coq programming, it also providessupport for extracting Coq programs to Haskell.

5.2 Haskell and first-order logicLiquidHaskell LiquidHaskell [31] augments the Haskellprogramming language with refinement types: all types canbe coupled with a predicate that the inhabitants must satisfy.These refinements are then automatically checked by an SMTsolver; a successful solution means that all functions are totaland conform to these new, richer, specifications. In practice,when proving theorems in Coq, we can take advantage of amature environment and proof automation techniques; thiscan allow for faster verification than LiquidHaskell, given acorresponding Coq program [30].

Halo The prototype contract checker halo [32] takes aHaskell program, uses GHC to desugar it into the intermedi-ate language Core, and then translates the Core program intoa first-order logic formula. It then invokes an SMT solversuch as Z3 [10] or Vampire [28] to prove this formula; a suc-cessful proof tells us that the original program is crash-free.

5.3 Translating Haskell to higher-order logicHaskabelle In the Isabelle/HOL ecosystem, hs-to-coq hasa direct correspondence in Haskabelle [14], which translates

11

Page 12: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

1211

1212

1213

1214

1215

1216

1217

1218

1219

1220

1221

1222

1223

1224

1225

1226

1227

1228

1229

1230

1231

1232

1233

1234

1235

1236

1237

1238

1239

1240

1241

1242

1243

1244

1245

1246

1247

1248

1249

1250

1251

1252

1253

1254

1255

1256

1257

1258

1259

1260

1261

1262

1263

1264

1265

CPP’18, January 2018, Los Angeles, CA, USA Spector-Zabusky, Breitner, Rizkallah, and Weirich

1266

1267

1268

1269

1270

1271

1272

1273

1274

1275

1276

1277

1278

1279

1280

1281

1282

1283

1284

1285

1286

1287

1288

1289

1290

1291

1292

1293

1294

1295

1296

1297

1298

1299

1300

1301

1302

1303

1304

1305

1306

1307

1308

1309

1310

1311

1312

1313

1314

1315

1316

1317

1318

1319

1320

total Haskell code into equivalent Isabelle function defini-tions. Like our tool, it parses Haskell, desugars syntacticconstructs, configurably adapts basic types and functions totheir counterpart in Isabelle’s standard library. It used to bebundled with the Isabelle release, but it has not been updatedrecently and was dropped from Isabelle.

While Isabelle/HOL is, like Coq, a logic of total functions,all types in HOL are non-empty and inhabited by the poly-morphic value undefined. Therefore, Haskabelle can trans-late partial patterns like described in Section 3.4, but withoutintroducing inconsistency by relying on axioms.Haskabelle supports boolean guards in simple cases, but

does not implement fall-through across patterns on guardfailure. In particular, the take function shown in Section 3.4would be translated to a function that is undefined whenn > 0.

HOLCF-Prelude A translation of Haskell into a total logic,as performed by hs-to-coq and Haskabelle, necessarilyhides the finer semantic nuances that arise due to laziness,and does not allow reasoning about partially defined or infi-nite values. If that is a concern, one might prefer a translationinto the Logic of Computable Functions (LCF) [26], whereevery type is a domain and every function is continuous.LCF is, for example, implemented in Isabelle’s HOLCF pack-age [16, 23]. Parts of the Haskell standard library have beenmanually translated into this setting [5] and used to verifythe rewrite rules applied by HLint, a Haskell style checker.

seL4 Haskell has been used as a prototyping language forformally verified systems in the past. The seL4 verified mi-crokernel started with a Haskell prototype that was semi-automatically translated to Isabelle/HOL [11]. As in ourwork,they were restricted to the terminating fragment of Haskell.The authors found that the availability of the Haskell

prototype provided a machine-checkable formal executablespecification of the system. They used this prototype to refinetheir designs via testing, allowing them to make correctionsbefore full verification. In the end, they found that startingwith Haskell lead to a “productive, highly iterative devel-opment” contributing to a “mature final design in a shorterperiod of time.”

5.4 Haskell and dependently-typed languagesProgrammatica/Alfa The Programmatica project [15] in-cluded a tool to translate Haskell into the proof editor Alfa.As in our work, their tool only produces valid proofs fortotal functions over finite data structures. They state: “Whenthe translation falls outside that set, any correctness proofsconstructed in Alfa entail only partial correctness, and weleave it to the user to judge the value of such proofs.”

The logic of the Alfa proof assistant is based on dependenttype theory, but without as many features as Coq. In particu-lar, the Programmatica tool compiles away type classes andnested pattern matching, features retained by hs-to-coq.

Agda 1 Dyber, Haiyan, and Takeyama [12] developed atool for automatically translating Haskell programs to theAgda/Alfa proof assistant. Their solution to the problemof partial pattern matching is to synthesize predicates thatdescribe the domain of functions. They explicitly note theinterplay between testing and theorem proving and showhow to verify a tautology checker.

Agda 2 Abel et al. [1] translate Haskell expressions intothe logic of the Agda 2 proof assistant. Their tool workslater in the GHC pipeline than ours; instead of translatingHaskell source code, they translate Core expressions. Coreis an explicitly typed internal language for Haskell used byGHC, where type classes, pattern matching and many formsof syntactic sugar have been compiled away.

Their translation explicitly handles partiality by introduc-ing a monad for partial computation. Total code is actuallypolymorphic over the monad in which it should execute,allowing the monad to be instantiated by the identity monador the Maybe monad as necessary. Agda’s predicativity alsocauses issues with the translation of GHC’s impredicative,System F-based core language.

5.5 Translating other languages to CoqChargueraud’s CFML [6] translates OCaml source code tocharacteristic formulae expressed as Coq axioms. This sys-tem has been used to verify many of the functional programsfrom Okasaki’s Purely Functional Data Structures [24].

6 Conclusions and future workWe presented a methodology for verifying Haskell pro-grams, built around translating them into Coq with thehs-to-coq tool. We successfully applied this methodologyto pre-existing code in multiple case studies, as well as inthe ongoing process of providing the base Haskell libraryfor these and other examples to build on.

Looking forward, there are always more Haskell featuresthat we can extend the tool to support; we plan to applythis tool to larger real-world software projects and will usethat experience to prioritize our next steps. We also wouldlike to develop a Coq tactic library that can help automatereasoning about the patterns found in translated Haskellcode as well as extend the proof theory of our base library.

AcknowledgmentsThanks to John Wiegley for discussion and support, and toLeonidas Lampropoulos and Jennifer Paykin for their helpfulcomments.

References[1] Andreas Abel, Marcin Benke, Ana Bove, John Hughes, and Ulf Norell.

2005. Verifying Haskell Programs Using Constructive Type Theory. InHaskell Workshop. ACM, 62–73. DOI:http://dx.doi.org/10.1145/1088348.1088355

12

Page 13: Total Haskell is Reasonable Coq - Computer Science and ...crizkallah/publications/hs_to_coq.pdf · Haskell development is backed by an industrial-strength compiler (the Glasgow Haskell

1321

1322

1323

1324

1325

1326

1327

1328

1329

1330

1331

1332

1333

1334

1335

1336

1337

1338

1339

1340

1341

1342

1343

1344

1345

1346

1347

1348

1349

1350

1351

1352

1353

1354

1355

1356

1357

1358

1359

1360

1361

1362

1363

1364

1365

1366

1367

1368

1369

1370

1371

1372

1373

1374

1375

Total Haskell is Reasonable Coq CPP’18, January 2018, Los Angeles, CA, USA

1376

1377

1378

1379

1380

1381

1382

1383

1384

1385

1386

1387

1388

1389

1390

1391

1392

1393

1394

1395

1396

1397

1398

1399

1400

1401

1402

1403

1404

1405

1406

1407

1408

1409

1410

1411

1412

1413

1414

1415

1416

1417

1418

1419

1420

1421

1422

1423

1424

1425

1426

1427

1428

1429

1430

[2] Gilles Barthe, Julien Forest, David Pichardie, and Vlad Rusu. 2006.Defining and Reasoning About Recursive Functions: A Practical Toolfor the Coq Proof Assistant. In FLOPS (LNCS), Vol. 3945. Springer,114–129. DOI:http://dx.doi.org/10.1007/11737414_9

[3] Joachim Breitner. 2017. successors: An applicative functor to managesuccessors. https://hackage.haskell.org/package/successors-0.1. (1February 2017).

[4] Joachim Breitner, Richard A. Eisenberg, Simon L. Peyton Jones, andStephanie Weirich. 2014. Safe zero-cost coercions for Haskell. In ICFP.ACM, 189–202. DOI:http://dx.doi.org/10.1145/2628136.2628141

[5] Joachim Breitner, Brian Huffman, Neil Mitchell, and Christian Ster-nagel. 2013. Certified HLints with Isabelle/HOLCF-Prelude. In Haskelland Rewriting Techniques (HART). arXiv:1306.1340

[6] Arthur Charguéraud. 2010. Program verification through characteris-tic formulae. In ICFP. ACM, 321–332. DOI:http://dx.doi.org/10.1145/1932681.1863590

[7] Haogang Chen, Daniel Ziegler, Tej Chajed, Adam Chlipala, M. FransKaashoek, and Nickolai Zeldovich. 2015. Using Crash Hoare Logicfor Certifying the FSCQ File System. In SOSP. ACM, 18–37. DOI:http://dx.doi.org/10.1145/2815400.2815402

[8] Haskell Core Libraries Comittee. 2017. base: Basic libraries. https://hackage.haskell.org/package/base-4.9.1.0. (14 January 2017).

[9] Nils Anders Danielsson, John Hughes, Patrik Jansson, and JeremyGibbons. 2006. Fast and loose reasoning is morally correct. In POPL.ACM, 206–217. DOI:http://dx.doi.org/10.1145/1111037.1111056

[10] Leonardo Mendonça de Moura and Nikolaj Bjørner. 2008. Z3: AnEfficient SMT Solver. In TACAS (LNCS), Vol. 4963. Springer, 337–340.DOI:http://dx.doi.org/10.1007/978-3-540-78800-3_24

[11] Philip Derrin, Kevin Elphinstone, Gerwin Klein, David Cock, andManuel M. T. Chakravarty. 2006. Running the Manual: An Approachto High-assurance Microkernel Development. In Haskell Symposium.ACM, 60–71. DOI:http://dx.doi.org/10.1145/1159842.1159850

[12] Peter Dybjer, Qiao Haiyan, and Makoto Takeyama. 2004. VerifyingHaskell programs by combining testing, model checking and interac-tive theorem proving. Information & Software Technology 46, 15 (2004),1011–1025. DOI:http://dx.doi.org/10.1016/j.infsof.2004.07.002

[13] Georges Gonthier, Assia Mahboubi, and Enrico Tassi. 2016. A SmallScale Reflection Extension for the Coq system. Research Report RR-6455.Inria Saclay Ile de France. https://hal.inria.fr/inria-00258384

[14] Florian Haftmann. 2010. From higher-order logic to Haskell: thereand back again. In ’10. ACM, 155–158. DOI:http://dx.doi.org/10.1145/1706356.1706385

[15] Thomas Hallgren, James Hook, Mark P. Jones, and Richard B. Kieburtz.2004. An overview of the programatica toolset. In HCSS.

[16] Brian Huffman. 2012. HOLCF ’11: A Definitional Domain Theory forVerifying Functional Programs. Ph.D. Dissertation. Portland State Uni-versity. DOI:http://dx.doi.org/10.15760/etd.113

[17] Graham Hutton. 2016. Programming in Haskell (2nd ed.). Cam-bridge University Press. 241–246 pages. DOI:http://dx.doi.org/10.1017/CBO9780511813672

[18] Adam Megacz Joseph. 2014. Generalized Arrows. (May 2014). http://www2.eecs.berkeley.edu/Pubs/TechRpts/2014/EECS-2014-130.htmlalso see http://www.megacz.com/berkeley/coq-in-ghc/.

[19] Pierre Letouzey. 2002. A New Extraction for Coq. In TYPES(LNCS), Vol. 2646. Springer, 200–219. DOI:http://dx.doi.org/10.1007/3-540-39185-1_12

[20] Simon Marlow (Ed.). 2010. Haskell 2010 Language Report.[21] Simon Marlow and Simon Peyton Jones. 2012. The Glasgow Haskell

Compiler. In The Architecture of Open Source Applications, Volume 2.Lulu. http://www.aosabook.org/en/ghc.html

[22] The Coq development team. 2016. The Coq proof assistant referencemanual. LogiCal Project. http://coq.inria.fr Version 8.6.1.

[23] Olaf Müller, Tobias Nipkow, David von Oheimb, and Oskar Slotosch.1999. HOLCF = HOL + LCF. Journal of Functional Programming 9

(1999), 191–223. DOI:http://dx.doi.org/10.1017/S095679689900341X[24] Chris Okasaki. 1999. Purely functional data structures. Cambridge

University Press. DOI:http://dx.doi.org/10.1017/CBO9780511530104[25] Will Partain. 1996. GHC commit 6c381e873e. http://git.haskell.org/

ghc.git/commit/6c381e873e. (19 March 1996).[26] Lawrence C. Paulson. 1987. Logic and Computation: Interactive Proof

with Cambridge LCF. Cambridge University Press. DOI:http://dx.doi.org/10.1017/CBO9780511526602

[27] Matthew Pickering, Gergo Érdi, Simon Peyton Jones, and Richard A.Eisenberg. 2016. Pattern synonyms. In Haskell. ACM, 80–91. DOI:http://dx.doi.org/10.1145/2976002.2976013

[28] Alexandre Riazanov and Andrei Voronkov. 1999. Vampire. In CADE-16(LNCS), Vol. 1632. Springer, 292–296. DOI:http://dx.doi.org/10.1007/3-540-48660-7_26

[29] Matthieu Sozeau and Nicolas Oury. 2008. First-Class Type Classes. InTPHOLs (LNCS), Vol. 5170. Springer, 278–293. DOI:http://dx.doi.org/10.1007/978-3-540-71067-7_23

[30] Niki Vazou, Leonidas Lampropoulos, and Jeff Polakow. 2017. A Tale ofTwo Provers: Verifying Monoidal String Matching in Liquid Haskelland Coq. In Haskell Symposium. ACM, 63–74. DOI:http://dx.doi.org/10.1145/3122955.3122963

[31] Niki Vazou, Eric L. Seidel, Ranjit Jhala, Dimitrios Vytiniotis, and SimonPeyton-Jones. 2014. Refinement Types for Haskell. In ICFP. ACM,269–282. DOI:http://dx.doi.org/10.1145/2628136.2628161

[32] Dimitrios Vytiniotis, Simon Peyton Jones, Koen Claessen, and DanRosén. 2013. HALO: Haskell to Logic Through Denotational Seman-tics. In POPL. ACM, 431–442. DOI:http://dx.doi.org/10.1145/2429069.2429121

[33] Philip Wadler and Stephen Blott. 1989. How to Make ad-hoc Polymor-phism Less ad-hoc. In POPL. ACM, 60–76. DOI:http://dx.doi.org/10.1145/75277.75283

[34] John Wiegley. 2017. coq-haskell: A library for formalizing Haskelltypes and functions in Coq. https://github.com/jwiegley/coq-haskell.(2017).

13