Skip to content

Instantly share code, notes, and snippets.

@memotype
Created August 9, 2024 05:08
Show Gist options
  • Save memotype/92d0dcdbdf0c002a41e8c49f05bde7ea to your computer and use it in GitHub Desktop.
Save memotype/92d0dcdbdf0c002a41e8c49f05bde7ea to your computer and use it in GitHub Desktop.
In OCaml, it is conventional for modules to define functions that return a new value of the module's main type using a variety of naming patterns, depending on the context and the specific purpose of the function. The most common naming patterns are:
1. **`make`**: This is the most common and general naming convention for functions that construct new values of a type. For example:
- `Array.make` creates a new array with a specified size and initializes all elements to a given value.
- `List.make` could theoretically be used to create a list with a given size and default value (though OCaml's standard `List` module doesn't provide this).
The `make` function usually indicates that the function will create a new instance of the type, often taking parameters to initialize it.
2. **`create`**: Similar to `make`, but often used when the function is more complex, perhaps involving more parameters or more intricate initialization logic. For example:
- `Hashtbl.create` initializes a new hash table with a specified initial size.
- `Buffer.create` initializes a new buffer with a specified initial size.
The `create` function often implies that there might be some setup involved, beyond just setting a size or value.
3. **Specialized Creation Functions**: Sometimes, a module might provide specialized creation functions tailored to specific use cases. For example:
- `Array.create_float` is a specialized function that creates an array optimized for storing floats, as floats in OCaml have special memory considerations.
- A module might offer functions like `create_with_capacity` or `create_from_list` to clarify the specific intent or optimization.
4. **`init`**: This is another common convention, especially when the creation process involves generating elements or initializing them based on a function or some form of iteration. For example:
- `Array.init` creates an array and initializes its elements using a function.
- `List.init` (if implemented) would similarly create a list with elements generated by a function.
5. **Anonymous Struct Creation**: For record types and other more complex structures, it is common to define values directly rather than through a function. However, in more abstract modules where you don't want to expose the underlying structure, a `make`, `create`, or other function would be used to abstract away the construction details.
### Best Practices
- **Use `make` for simplicity**: If the creation of the type is straightforward, with no complex initialization required, `make` is usually preferred.
- **Use `create` for more complex or customizable construction**: If you expect users to need more flexibility or when the creation process is not trivial, use `create`.
- **Provide specialized functions when needed**: If there's a clear need for different types of creation methods, provide them with clear and descriptive names.
- **Leverage `init` when initializing with a function**: When generating elements based on a function or a computation, `init` is a common choice.
### Example
If you were defining a simple stack module, you might have:
```ocaml
module Stack = struct
type 'a t = { mutable elements : 'a list }
let make () = { elements = [] }
let push x s = s.elements <- x :: s.elements
let pop s = match s.elements with
| [] -> None
| x :: xs -> s.elements <- xs; Some x
end
```
Here, `make` is used for a straightforward, empty stack creation. If you had a more complex stack that required some setup (like a pre-populated stack or one with a specific size), you might use `create` instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment