[erlang-questions] -spec Enhancement: -spec f :: ftype().

David Mercer <>
Wed Feb 15 15:43:18 CET 2012


Yes, custom behaviors is definitely one of the primary use cases, though,
for me , any time you have functions that take functions as arguments, it
would be nice to be able to specify a function as having a certain type,
rather than having to repeat yourself in the -spec.

 

From: Tim Watson [mailto:] 
Sent: Tuesday, February 14, 2012 1:41 PM
To: David Mercer
Cc: 
Subject: Re: [erlang-questions] -spec Enhancement: -spec f :: ftype().

 

On 14 February 2012 16:14, David Mercer <> wrote:

You know, I've always wished I could declare a function type (e.g., -type
f() :: fun((arg_type1(), arg_type2()) -> return_type())) and then declare a
function as conforming to that spec (e.g., -spec f1 :: f()), instead of
having to repeat myself (i.e., -spec f1(arg_type1(), arg_type2()) ->
return_type()).  This helps when the function type is quite complicated and
needs to be repeated often, and also when you have a function that takes a
function with a certain spec, when you define the actual functions that can
be passed to it.  What do y'all think?  Am I crazy?

 

 

I wonder if, combined with custom behaviours, that might be a step in the
direction of being able to provide a consistent interface to modules that
offer equivalent functionality (database connectivity, config file handling,
logging, etc). I'm assuming that is the primary use case, although I have a
feeling you have encountered other scenarios from what you've said.

 

I think it sounds quite handy, although presumably you'd want to export the
type from a module somewhere, in which case it gets scoped to an application
and it *could* get hairy trying to figure out the right place to put it.
Pushing it down to the appropriate scope would then make it less appropriate
to re-use across modules/components.

 

For example, consider a concrete example specification:

 

    -opaque dbms_connection() :: #dbms_connection_handle{}.  %% with some
fields for vendor specific stuff

    -type dbms_connection_info() :: #dbms_connection_info{}.  %% with some
fields for vendor specific stuff

    -type open_connection(connection_info()) -> dbms_connection() | {error,
term()}.

 

Where should I export this from? Do I have some 'core' module (say, the
primary or application callback module for the app/library) where I do this?


 

    %% in dbms.erl

    -include("dbms.hrl").

    -export_type([open_connection/1]).

 

And then presumably I'm going to use this in a few different places. It'd be
fine, I suppose, to see something like

 

    -spec open_connection() :: dbms:open_connection()

    open_connection(CInfo) -> .....

 

But then if I start to use that across multiple applications, does it still
make sense?

 

    -module(pgsql_connection).

    -spec open_connection() :: dbms:open_connection()

    open_connection(CInfo) -> .....

    

I suppose that reads ok, but why not just put it into a header file as a
sepc and distribute that? If that's the case, I can have a library
application carry the header and just use -spec instead. Any implementor can
simply include the header and reference the spec:

 

    %% in application/library dbms_api/include/dbms_api.hrl

    -opaque dbms_connection() :: #dbms_connection_handle{}.  %% with some
fields for vendor specific stuff

    -type dbms_connection_info() :: #dbms_connection_info{}.  %% with some
fields for vendor specific stuff

    -spec open_connection(connection_info()) -> dbms_connection() | {error,
term()}.

 

    %% in application dbms_pgsql/pgsql.erl

 

    -include_lib("dbms_api/include/dbms_api.hrl").

 

    %% we 'inherit' the -spec from our library dependency on dbms_api

    open_connection(CInfo) ->

        dbms_pgsql_connection:open(CInfo).

 

 

So here I'm not sure what the value is. If we're thinking about three or
four functions that have the same signature in terms of input arguments and
return type, but have different names, then Kostis correct me if I'm wrong
but I think it'd be pretty tough to do something like that because the
function name *is* part of the signature. 

 

You *could* experiment with this idea by writing a parse_transform that
creates the -spec in response to a user defined annotation
(https://github.com/hyperthunk/annotations) but I'm not sure if (a) the
-type and -spec data will appear during the parse_transform execution or (b)
how you export a -spec from a parse_transform, or if this (b) is even
possible.

 

Another approach might be to write a build plugin (for rebar or whatever)
that reads a bunch of metadata and generates the specs accordingly. Yet
another approach might be to provide a hint to TypEr and let it generate the
-specs for you. 

 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20120215/d6d4aaa4/attachment.html>


More information about the erlang-questions mailing list