Declarative Function Naming
Asked Answered
C

2

7

How can functions be named to clearly reflect that they follow a declarative paradigm?

Context: I've recently started working on creating libraries that work in a declarative manner but I'm having a hard time coming up with a naming convention that reflects it. In the past I've created imperative functions with names like createThing, but I'm finding it difficult to succinctly convey the idea of "do what is necessary to return a Thing that looks like ____" in a function name.
Ideally, I'd like to follow a formal standard or established naming convention. Otherwise, I'm hoping to at least find some guidance from pre-existing codebases.

Cusick answered 23/10, 2019 at 0:32 Comment(3)
I'd say that "createThing" is more declarative than imperative as it is not explicitly stating how the thing will be created.Ileanaileane
doThis or createThat are grammatically imperative style. A declarative style would either describe from what a thing would be composed of, with passive voice, aThingIsComposed or with an -ing form to reify the action of creating creatingAThingAnthe
In Prolog which is declarative, one should think in terms of relationships and create names accordingly. So seeing names that work in only one direction is not a relationship but an action and thus not the goal. If you can declaratively create something then you should also be able to declaratively destroy something and in Prolog that would be a relation and done with just one predicate, and naming it like ...Create or Create... is only showing one side of the declaration. I know this is very confusing; perhaps one should program in Prolog.Winegrower
J
5

Given your concerns to have a succinct function name, I would look into whether your createThing function does too much and split it into a few smaller chunks (this is heavily influenced by C# syntax):

var yourThing = new Thing()
    .with(new PropertyA()).thenWith(new DependentPropertyOfA()) // digress a bit
    .with(new PropertyB()) // back to main thread here
    .withPieceOfLogic((parameter1, parameter2) => {define some logic here}) // so you can potentially can swap implementations as well
    .create();

Here I'm aiming at something along the lines of FluentInterface. This might get you to the aesthetics you're looking for.

One thing to bear in mind with this approach, this chaining makes it heavily linear and might not work well if you need to make detours from defining your main object a lot.

Another few examples to draw inspiration from:

  1. https://www.entityframeworktutorial.net/efcore/fluent-api-in-entity-framework-core.aspx
  2. https://momentjs.com/docs/.
  3. https://jasmine.github.io/tutorials/your_first_suite
Jazmin answered 8/12, 2019 at 3:37 Comment(0)
V
3

In my experience there is no canonical naming scheme for primary functions in declarative frameworks, but there are many prominent examples you could draw inspiration from.

Methods associated with the FluentInterface style are often prefixed 'with'. e.g.

new HTTPClient()
   .withProxy(new Proxy('localhost', 8080))
   .withTimeOut(Duration.of("30s"))
   .withRequestHeaders(
     new Headers()
        .with('User-Agent', 'FluidClient')
   );

See https://martinfowler.com/bliki/FluentInterface.html

Some FluentInterface designs do away with function name prefixes and just name functions directly after the declarative element they represent. An example from JOOQ:(https://www.jooq.org/doc/3.12/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/)

String sql = create.select(field("BOOK.TITLE"), field("AUTHOR.FIRST_NAME"), 
                         field("AUTHOR.LAST_NAME"))
                   .from(table("BOOK"))
                   .join(table("AUTHOR"))
                   .on(field("BOOK.AUTHOR_ID").eq(field("AUTHOR.ID")))
                   .where(field("BOOK.PUBLISHED_IN").eq(1948))
                   .getSQL();

This has the benefit of making a chain of imperative invocations read like a declarative DSL. However eschewing naming conventions for methods can make the source for the builder class less readable.

The above examples are of builders being used to construct objects where encapsulated state is used to represent the concept being declared. Some OO frameworks reduce this further so that the code is composed solely in terms of constructors for 'fine-grained' objects. In most c-derived languages constructors are required to be named after the type they are associated with.

An example of a UI widget tree being declared in Flutter (from https://flutter.dev/docs/development/ui/widgets-intro#using-material-components):

 return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.menu),
          tooltip: 'Navigation menu',
          onPressed: null,
        ),
        title: Text('Example title'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.search),
            tooltip: 'Search',
            onPressed: null,
          ),
        ],
      ),
      // body is the majority of the screen.
      body: Center(
        child: Text('Hello, world!'),
      ),
      floatingActionButton: FloatingActionButton(
        tooltip: 'Add', // used by assistive technologies
        child: Icon(Icons.add),
        onPressed: null,
      ),
    );
Veto answered 6/12, 2019 at 3:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.