OCaml Style Guide Fall 2017

Similar documents
Racket Style Guide Fall 2017

Lab 7: OCaml 12:00 PM, Oct 22, 2017

Homework 8: Matrices Due: 11:59 PM, Oct 30, 2018

Scala Style Guide Spring 2018

Lab 10: OCaml sequences, comparators, stable sorting 12:00 PM, Nov 12, 2017

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

CMSC 330: Organization of Programming Languages. OCaml Expressions and Functions

(Provisional) Lecture 08: List recursion and recursive diagrams 10:00 AM, Sep 22, 2017

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

Background. CMSC 330: Organization of Programming Languages. Useful Information on OCaml language. Dialects of ML. ML (Meta Language) Standard ML

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

Dialects of ML. CMSC 330: Organization of Programming Languages. Dialects of ML (cont.) Features of ML. Functional Languages. Features of ML (cont.

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341)

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

The Substitution Model

CS3110 Spring 2016 Lecture 6: Modules

(Provisional) Lecture 22: Rackette Overview, Binary Tree Analysis 10:00 AM, Oct 27, 2017

Tuples. CMSC 330: Organization of Programming Languages. Examples With Tuples. Another Example

The Substitution Model. Nate Foster Spring 2018

OCaml Data CMSC 330: Organization of Programming Languages. User Defined Types. Variation: Shapes in Java

SMURF Language Reference Manual Serial MUsic Represented as Functions

Products and Records

Good Coding Practices Spring 2018

Supplement D: Expanded Guidelines on Programming Style and Documentation

Expanded Guidelines on Programming Style and Documentation

11 Coding Standards CERTIFICATION OBJECTIVES. Use Sun Java Coding Standards

Haskell Syntax in Functions

Objectives. Chapter 4: Control Structures I (Selection) Objectives (cont d.) Control Structures. Control Structures (cont d.) Relational Operators

CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Winter 2013

SNU Programming Language Theory

Intermediate Code Generation

Functional abstraction. What is abstraction? Eating apples. Readings: HtDP, sections Language level: Intermediate Student With Lambda

Functional abstraction

OCaml. ML Flow. Complex types: Lists. Complex types: Lists. The PL for the discerning hacker. All elements must have same type.

Lecture 5: Implementing Lists, Version 1

A Second Look At ML. Chapter Seven Modern Programming Languages, 2nd ed. 1

A First Look at ML. Chapter Five Modern Programming Languages, 2nd ed. 1

Functions & First Class Function Values

Chapter 4: Control Structures I (Selection) Objectives. Objectives (cont d.) Control Structures. Control Structures (cont d.

CMSC 330: Organization of Programming Languages

Lecture 2: SML Basics

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

An introduction introduction to functional functional programming programming using usin Haskell

In Java, data type boolean is used to represent Boolean data. Each boolean constant or variable can contain one of two values: true or false.

CSCI-GA Scripting Languages

CMSC 330: Organization of Programming Languages. OCaml Imperative Programming

CS 321 Programming Languages

These notes are intended exclusively for the personal usage of the students of CS352 at Cal Poly Pomona. Any other usage is prohibited without

Introduction to Typed Racket. The plan: Racket Crash Course Typed Racket and PL Racket Differences with the text Some PL Racket Examples

Scheme: Data. CS F331 Programming Languages CSCE A331 Programming Language Concepts Lecture Slides Monday, April 3, Glenn G.

CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Fall 2011

(Provisional) Lecture 32: Estimated Value, Minimax, Functional Data 10:00 AM, Nov 20, 2017

Style guide for Standard ML programmers

Scheme as implemented by Racket

Module 8: Local and functional abstraction

Introduction to OCaml

(Provisional) Lecture 20: OCaml Fun!

CSE 142/143 Unofficial Style Guide

Practical Haskell. An introduction to functional programming. July 21, Practical Haskell. Juan Pedro Villa-Isaza. Introduction.

Some Advanced ML Features

CS558 Programming Languages

CS 342 Lecture 7 Syntax Abstraction By: Hridesh Rajan

Lecture 2: Big-Step Semantics

SML A F unctional Functional Language Language Lecture 19

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

Mini-ML. CS 502 Lecture 2 8/28/08

MIDTERM EXAMINATION - CS130 - Spring 2003

The design recipe. Readings: HtDP, sections 1-5. (ordering of topics is different in lectures, different examples will be used)

The Environment Model. Nate Foster Spring 2018

CS 11 Haskell track: lecture 1

Homework 6: Higher-Order Procedures Due: 10:00 PM, Oct 17, 2017

CS 360 Programming Languages Interpreters

1 Lexical Considerations

CS558 Programming Languages

CS2112 Fall Assignment 4 Parsing and Fault Injection. Due: March 18, 2014 Overview draft due: March 14, 2014

Scheme: Expressions & Procedures

Chapter 9: Dealing with Errors

CS 360: Programming Languages Lecture 10: Introduction to Haskell

CS115 - Module 4 - Compound data: structures

Functional Programming. Pure Functional Programming

Functions. Nate Foster Spring 2018

CS 142 Style Guide Grading and Details

CMSC 330: Organization of Programming Languages

News. Programming Languages. Recap. Recap: Environments. Functions. of functions: Closures. CSE 130 : Fall Lecture 5: Functions and Datatypes

The Design Recipe Fall 2017

COSE212: Programming Languages. Lecture 3 Functional Programming in OCaml

Makefiles Makefiles should begin with a comment section of the following form and with the following information filled in:

C++ Style Guide. 1.0 General. 2.0 Visual Layout. 3.0 Indentation and Whitespace

6.001 Notes: Section 6.1

SML Style Guide. Last Revised: 31st August 2011

User-defined Functions. Conditional Expressions in Scheme

So what does studying PL buy me?

CS115 - Module 9 - filter, map, and friends

CS 11 Ocaml track: lecture 2

News. Programming Languages. Complex types: Lists. Recap: ML s Holy Trinity. CSE 130: Spring 2012

Tail Calls. CMSC 330: Organization of Programming Languages. Tail Recursion. Tail Recursion (cont d) Names and Binding. Tail Recursion (cont d)

Homework 6: Higher-Order Procedures Due: 11:59 PM, Oct 16, 2018

Formal Semantics. Prof. Clarkson Fall Today s music: Down to Earth by Peter Gabriel from the WALL-E soundtrack

Algorithms and Programming I. Lecture#12 Spring 2015

CMSC 330: Organization of Programming Languages. OCaml Data Types

Transcription:

CS17 Integrated Introduction to Computer Science Hughes OCaml Style Guide Fall 2017 Contents 1 Introduction 1 2 Naming 1 3 Formatting 2 3.1 Indenting.......................................... 2 3.2 Line Breaks......................................... 4 3.3 Parentheses......................................... 4 3.4 Spacing........................................... 6 4 Pattern Matching 6 5 Verbosity 10 6 Acknowledgments 11 1 Introduction While the OCaml language supports functional, imperative, and object-oriented programming constructs, this style guide only covers aspects of OCaml related to the functional programming features used in CS 17. All the OCaml code you write in CS 17 must follow all the guidelines spelled out in this document. Furthermore, all the OCaml code you write beyond CS 17 should continue to follow the guidelines spelled out in this document, and/or those spelled out by OCaml s developers at INRIA: http://caml.inria.fr/resources/doc/guides/guidelines.en.html. 2 Naming The following are the identifier naming guidelines that are followed by the OCaml library. You should abide by these conventions in CS 17:

Identifier Type Convention Constants and Procedures Initial letter must be lower case. Use underscores for multiword names. Example: km_to_miles Predicates You cannot end predicate names with a?. Instead, use P, like memberp. Or is, like is_empty. Types All letters should be lower case. Use underscores for multiword names. Example: priority_queue Constructors Initial letter must be upper case. Use embedded caps for multiword names. Historic exceptions are true and false. Examples: Node EmptyQueue Module Types Initial letter must be upper case. Use all caps, and underscores for multiword names. Example: PRIORITY_QUEUE Modules Initial letter must be upper case. Use embedded caps for multiword names. Example: PriorityQueue Functors Same as module type convention. Example: PriorityQueue 3 Formatting In most sections of an OCaml program, how you use white space is not mandated by the compiler. Its use, then, comes down to style. 3.1 Indenting If you happen to use ocaml-top, it indents your code for you, so you can gloss over this section. But if you use another text editor, you must follow the conventions set forth in this section. Indent most lines by two spaces. Lines that indent code should do so by two spaces more than the previous line of code. For example: let proc (foo : int) (bar : int) : int = foo * bar let proc (foo : int) (bar : int) : int = foo * bar Lines that wrap around should indent by more. For example: 2

let x = ("This is a really long string that is intentionally" ^ "longer than 80 chracters, and hence cannot possibly" ^ "fit on one, or even two, lines.") How to indent nested let expressions. Nested let expressions should not be indented. Nor should let and expressions. let x = exp1 in let y = exp2 in x + y let x = exp1 in let y = exp2 in x + y (* Also Good *) let x = exp1 and y = exp2 in x + y (* Still Good *) let x = very_very_long_exp in x + y In this last example, it makes sense to put in at the start of a line because it could get lost in the shuffle at the end of a very very long expression. How to indent match expressions. Align match expressions as follows: match expr with pat1 -> pat2 -> How to indent if expressions. Indent if expressions using one of these options, depending on the length of the expressions: if exp1 then exp2 else exp3 if exp1 then exp2 else exp3 if exp1 then exp2 else exp3 In the first example, the expressions are short enough to all fit on one line. In the second example, the first two expressions are short enough to fit on the same line. In the third example, the expressions are too long to even fit two of them on the same line. 3

Here is an example of nested if expressions: if exp1 then exp2 else if exp3 then exp4 else exp5 How to indent comments. Comments should be indented to the level of the line of code to which the comment refers usually, the line that follows the comment. 3.2 Line Breaks If you have a long line of code with parallel structure, breaking it up can improve readability. Pattern matching is a perfect example of this. The following is functional code, but it s hard to read: match aloi with [] -> -15 hd :: tl when hd > 0 -> 17 * hd hd :: tl -> 0 This is much better: match aloi with [] -> -15 hd :: tl when hd > 0 -> 17 * hd hd :: tl -> 0 This same idea also arises when you are dealing with a compound data structure such as a tree. Understanding its form when it is written linearly instead of structurally can be difficult: let my_tree = Node (17, Node (18, Node (19, Leaf, Leaf), Node (22, Node (33, Leaf, Leaf), Leaf)), Leaf) So breaking it up across multiple lines that reflect its structure is usually a good idea: let my_tree = Node (17, Node (18, Node (19, Leaf, Leaf), Node (22, Node (33, Leaf, Leaf), Leaf)), Leaf) 3.3 Parentheses Use parentheses sparingly. Like in math, and unlike in Racket, the following expressions are equivalent in OCaml: 4

17 (17) ((17)) (((17))) ((((17)))) In these expressions, the use of parentheses is redundant. They do not change the semantics, and hence should be used sparingly (if at all). But, like in Racket, parentheses in OCaml often do have semantic content. They are used to construct tuples, to override built-in operator precedence, and to group structures into functor arguments. In these cases, parentheses are necessary, and hence, must be used. Here is an example of using parentheses to override built-in operator precedence: x+y * z+a (x + y) * (z + a) Spaces (and indentation) do not achieve the effect of parentheses. The former of these two expressions computes x + (y * z) + a, which does not appear to be what was intended. Parenthesize to help indentation. Automated indentation algorithms are often assisted by parentheses. Consider the following: let x = "Long line" ^ "Another long line." let x = ("Long line" ^ "Another long line.") The latter informs an editor that the long line spills over onto another long line, so that the editor can indent it properly. Wrap nested match expressions with parentheses. It is almost always necessary to parenthesize nested match expressions, so it is good practice to do so by default. For example, the following code snippet looks like it should evaluate to 3 when digit1 is bound to 7, but it doesn t! (Instead, it evaluates to 3 when digit2 is bound to 7.) match digit1 with 1 -> match digit2 with 5 -> 1 6 -> 2 7 -> 3 Proper use of parentheses disambiguates this code: 5

match digit1 with 1 -> (match digit2 with 5 -> 1 6 -> 2) 7 -> 3 OCaml is not Java. Parentheses should never appear on a line by themselves, nor should they be the first graphical character on a line. 3.4 Spacing The space bar is your friend! Don t be afraid to press it. It is a nice big key, so it is easy to find. Use it. 1. Surround infix operators by spaces. Write this hd :: tl, not this hd::tl, and x + y, not x+y. 2. Insert spaces before and after the : in type annotation: e.g., (17 : int). (Why? Because : is an infix operator!) 3. Insert a space after a pipe in a type definition or a match expression. type season = Fall Winter Spring Summer 4. Insert a space after a type constructor: e.g., Hearts 7. 5. Insert a space before ;; at the end of each definition. On the other hand, just like you should not use too many parentheses, you should not insert too many spaces! For example, do not surround a procedure s arguments by parentheses and/or spaces. my_procedure ( arg_1 ) ( arg_2 ) my_procedure arg_1 arg_2 4 Pattern Matching Pattern matching should always follow the structure of the data. Suppose you define a variant type, such as: 6

type train_car = Engine Boxcar of int (* capacity *) Caboose When you pattern match on data of type train_car, your match expression should follow the structure of the data in the same order, like this: match train with Engine -> Boxcar n -> Caboose -> Also, your pattern matching should be exhaustive. This is not complete: match train with Engine -> Caboose -> If your pattern matching is incomplete, the OCaml compiler will issue a warning, as follows: Warning: this pattern-matching is not exhaustive. Treat such warnings as bugs! In some cases the compiler will flag a match as incomplete when actually it isn t. This is because the compiler is not smart enough to infer that all possible cases have been covered. For example, match t with Leaf -> Node (Leaf, datum, Leaf) Node (left, x, right) when (datum < x) -> Node (insert datum left, x, right) Node (left, x, right) when (datum > x) -> Node (left, x, insert datum right) Node (left, x, right) when (datum = x) -> Node (left, x, right) The incorrect way to eliminate this warning would be to simply add a catch-all case, as follows: Leaf -> Node (Leaf, datum, Leaf) Node (left, x, right) when (datum < x) -> Node (insert datum left, x, right) Node (left, x, right) when (datum > x) -> Node (left, x, insert datum right) Node (left, x, right) when (datum = x) -> Node (left, x, right) _ -> failwith "EMF" Here, EMF stands for Exhaustive Match Failure. The correct way to eliminate an compiler warning that arises from a failure to pattern match exhaustively is to add an explicit unguarded match as follows: Leaf -> Node (Leaf, datum, Leaf) Node (left, x, right) when (datum < x) -> Node (insert datum left, x, right) Node (left, x, right) when (datum > x) -> Node (left, x, insert datum right) Node (left, x, right) when (datum = x) -> Node (left, x, right) Node (left, x, right) -> failwith "EMF" 7

This latter approach is preferable because it preserves the ability of the compiler to flag unmatched cases, which is one of the key features of OCaml. In summary, never appease the compiler by inserting a catch-all. Doing so negates the power of the compiler, and will impede your power to debug and extend your code. Use pattern matching for selection. Instead of using fst and snd to deconstruct a tuple, use pattern matching. For example: type posn = float * float let p = some_posn in let x = fst p in let y = snd p in x +. y let x, y = some_posn in x +. y Similarly, records should be deconstructed using pattern matching: type circle = {center : posn, radius : float} let circ = some_circle in let c = circ.center in let r = circ.radius in let x = fst c in let y = snd c in r *. (x +. y) let {center = (x, y); radius = r} = some_circle in r *. (x +. y) You should also steer away from using List.hd and List.tl in favor of pattern matching. Pattern match using as few match expressions as possible. Rather than nest match expressions, you can often pattern match against a tuple. match month with Jan -> (match day with 1 -> "Happy New Year" _ -> "") Mar -> (match day with 14 -> "Happy Pi Day" _ -> "") 8

Oct -> (match day with 10 -> "Happy Metric Day" _ -> "") match month, day with Jan, 1 -> "Happy New Year" Mar, 14 -> "Happy Pi Day" Oct, 10 -> "Happy Metric Day" _ -> "" Never use only one pipe in a match expression. There is never a need for only one pipe in a match expression. In such cases, prefer let. match card with Hearts n -> let Hearts n = card in Pattern match a procedure s formal arguments when possible. In OCaml, compound types are deconstructed via pattern matching, using match expressions, let expressions, or by deconstructing a procedure s formal arguments. You should use the latter option if you need only the constituents of a procedure s formal arguments, and have no use for the arguments as a whole. In this example, the procedure s formal arguments are tuples: let f arg1 arg2 = let x = fst arg1 in let y = snd arg1 in let z = fst arg2 in let f (x, y) (z, _) = There is no need to name arg1 and arg2 here, since all that is needed are their constituents. Likewise, in this example, where the procedure s formal arguments are records: let f arg1 arg2 = let x = arg1.field1 in let y = arg1.field2 in let z = arg2.field3 in 9

let f {field1 = x; field2 = y} {field3 = z; field4 = _} = 5 Verbosity Simplify if expressions. There are a number of equivalent ways to express the same conditional logic. In almost all cases, shorter expressions are preferred: Verbose if expr then true else false if expr then false else true if expr then expr else false if not expr then x else y if x then true else y if x then y else false if x then false else y if x then y else true Concise expr not expr expr if expr then y else x x y x && y not x && y not x y When an if expression is used for argument selection, it can be embedded within a procedure application to improve readability, as follows: (* Duplication of f a b applications *) if c then f a b x else f a b y (* Can be eliminated by embedding the if *) f a b (if c then x else y) Don t rewrap procedures. When applying a procedure to another procedure, don t rewrap the procedure if it already does what you need it to do. Here are two examples: (* Verbose *) List.map (fun x -> sqrt x) [1.0; 4.0; 9.0; 16.0] (* Concise *) List.map sqrt [1.0; 4.0; 9.0; 16.0] (* Verbose *) List.fold_left (fun x y -> x + y) 0 (* Concise *) List.fold_left ( + ) 0 Don t misuse match expressions. Do not use match when you mean if! 10

match expr with true -> x false -> y if expr then x else y match expr with c -> x (* c is a constant *) _ -> y if e = c then x else y Don t overuse match expressions. Do not bind expressions unnecessarily. For example, do not bind something here, simply pattern match on it: let x = something in match x with match something with Similarly, do not use match to pattern match when let is enough: let x = match expr with (y, z) -> y let (x, _) = expr Don t reinvent the wheel. Built in to the OCaml library are a great number of ready-made procedures and data structures. You should use them, unless of course an assignment expressly forbids it! For example, when writing a procedure that recursively walks down a list, think fold! Some other data structures have similar folding procedures; use them when they are available. 6 Acknowledgments Much of this style guide was copied from parts of the CIS120 style guide at the University of Pennsylvania, which in turn copied much of its content of the CS312 style guide at Cornell University. 11

Please let us know if you find any mistakes, inconsistencies, or confusing language in this or any other CS17 document by filling out the anonymous feedback form: http://cs.brown.edu/courses/cs017/feedback. 12