Written by Federico Tomassetti
in Domain specific languages

    There are some ideas that are gaining popularity: blockchains and smart contracts are among them. If we can ignore the hype and the inflated expectations I think they maybe ideas with a strong potential impact.

    I do not want to talk about these technologies in general but just from the point of view of a language engineer. Now, every time language engineers meet and talk about these topics they can agree on something:

    The languages currently used to describe smart contracts are just terrible.

    Currently the most famous of these languages is Solidity that permits to describe contracts that can be executed automatically in the Ethereum blockchain. However Solidity does not resemble much a Domain Specific Language specifically intended to represents contracts: it just seems a common general purpose language.

    Is this sort of things what we want to use to write smart contracts?

    Is it possible to create better languages? Why should we bother doing so?

    Let’s talk about that.

    How traditional contracts work?

    You are probably familiar with traditional contracts, the ones written on a piece of paper or perhaps exchanged as PDFs. These contracts are written using natural languages (i.e., English, Italian, French). Inside them you can find conditions like:

    Company A will pay $X.000 to Company B within 30 days after receiving the delivery of the specified goods from Company B.

    How are this contracts enforced? If Company B delivers the goods and Company A does not pay what happens?

    It happens that the representatives of Company B will need to resolve the dispute, recurring to the judicial system or some form of arbitration. This sounds so much as 20th-century process… something lengthy, slow, burocratic and uncertain.

    The fact is that we are now living the time of automation and immediate satisfaction, so we need a different way of doing things. Nowadays you can have something delivered by Amazon within the day and still you have to wait for months or years for some old guy with a black robe to go over your contract and determine who is right, to enforce someone to pay or reimburse you, etc. Not very efficient, is it?

    The basic idea behind smart contracts is to have contracts that are automatically enforced and they are interpreted in an objective way. The clear advantages are the removal of costs from executing the contracts while at the same time getting better rapidity of execution and far greater predictability. In other words, in real-life contracts the infrastructure necessary to operate them comprise lawyers, notaries, judges, tribunals, injunctive orders, and so on. For “smart contracts” you need software running on a distributed network of computers.

    Guess what is cheaper and more efficient?

    A note on efficiency: I am not considering the fact that a huge amount of energy is wasted while mining new coins. I hope that whole stupid system can be fixed somehow. I am just focusing on the language designer perspective here.

    This is not as new as you may think…

    Now, today we got these mythological blockchains and the concept smart-contracts was popularized. This term is not new, it is just that nowadays we have an interesting piece of technology (the blockchain) which can enable this idea. The idea of smart contracts was already born before blockchains became a thing and, guess what, people have been thinking about ways to formalize contract for quite some time before that. There is a whole field called Computation Law which was started in the ’40. Yes, almost 80 years ago.

    I am not going to try to summarize over 80 years of research in a few lines. Anyway Wikipedia is doing a better job than me at that, but I think you should consider that an extensive amount of research and energy have been spent in this direction.

    So smart contracts are interesting only because they run on blockchains and blockchains offer some basic non-functional qualities. In particular they offer non-repudiability. It is like being recorded while doing something: you could not negate it afterwords.

    Limitations of smart contracts

    First of all we should consider one thing: smart contracts are limited and they can hardly replace all sorts of traditional contracts we have.

    Why? Because they are basically programs and can do things that programs can do.

    For example, they are perfect to exchange virtual assets. Do you want to write a contract to exchange different types of blockchain coins at a certain date if a given condition is met? Sure, it sounds like something a smart contract can do.

    Or you can use smart contracts to transfer data. For example, you can share keys that can be used to access a digital or a physical resource. For example you can send a digital code to open the door of an appartment.

    However you cannot write a contract that includes the exchange of physical goods. Sure, you can exchanges codes that could permit to access those physical goods. Or you could exchange codes that could be used to instruct some third party to start a delivery but you cannot involve human beings in the execution of the contract itself. The contract could use sources of information about the real world, like an API indicating the weather or the temperature in a certain area, but it cannot operate changes on the real world or measure them directly. For those sorts of things you have to use the good old traditional contracts.

    An example of “smart contract”

    Ok, you have probably seen a traditional contract, let’s now see how these smart contracts look like.

    Spoiler: they look terribly similar to code in a general purpose programming language and I think this is bad for the reasons we will discuss below.

    The example I am going to show is a smart contract expressed using Solidity. This contract defines a voting system.

    Take a look at it:

    pragma solidity ^0.4.16;
    
    /// @title Voting with delegation.
    contract Ballot {
        // This declares a new complex type which will
        // be used for variables later.
        // It will represent a single voter.
        struct Voter {
            uint weight; // weight is accumulated by delegation
            bool voted;  // if true, that person already voted
            address delegate; // person delegated to
            uint vote;   // index of the voted proposal
        }
    
        // This is a type for a single proposal.
        struct Proposal {
            bytes32 name;   // short name (up to 32 bytes)
            uint voteCount; // number of accumulated votes
        }
    
        address public chairperson;
    
        // This declares a state variable that
        // stores a `Voter` struct for each possible address.
        mapping(address => Voter) public voters;
    
        // A dynamically-sized array of `Proposal` structs.
        Proposal[] public proposals;
    
        /// Create a new ballot to choose one of `proposalNames`.
        function Ballot(bytes32[] proposalNames) public {
            chairperson = msg.sender;
            voters[chairperson].weight = 1;
    
            // For each of the provided proposal names,
            // create a new proposal object and add it
            // to the end of the array.
            for (uint i = 0; i < proposalNames.length; i++) {
                // `Proposal({...})` creates a temporary
                // Proposal object and `proposals.push(...)`
                // appends it to the end of `proposals`.
                proposals.push(Proposal({
                    name: proposalNames[i],
                    voteCount: 0
                }));
            }
        }
    
        // Give `voter` the right to vote on this ballot.
        // May only be called by `chairperson`.
        function giveRightToVote(address voter) public {
            // If the argument of `require` evaluates to `false`,
            // it terminates and reverts all changes to
            // the state and to Ether balances. It is often
            // a good idea to use this if functions are
            // called incorrectly. But watch out, this
            // will currently also consume all provided gas
            // (this is planned to change in the future).
            require(
                (msg.sender == chairperson) &&
                !voters[voter].voted &&
                (voters[voter].weight == 0)
            );
            voters[voter].weight = 1;
        }
    
        /// Delegate your vote to the voter `to`.
        function delegate(address to) public {
            // assigns reference
            Voter storage sender = voters[msg.sender];
            require(!sender.voted);
    
            // Self-delegation is not allowed.
            require(to != msg.sender);
    
            // Forward the delegation as long as
            // `to` also delegated.
            // In general, such loops are very dangerous,
            // because if they run too long, they might
            // need more gas than is available in a block.
            // In this case, the delegation will not be executed,
            // but in other situations, such loops might
            // cause a contract to get "stuck" completely.
            while (voters[to].delegate != address(0)) {
                to = voters[to].delegate;
    
                // We found a loop in the delegation, not allowed.
                require(to != msg.sender);
            }
    
            // Since `sender` is a reference, this
            // modifies `voters[msg.sender].voted`
            sender.voted = true;
            sender.delegate = to;
            Voter storage delegate_ = voters[to];
            if (delegate_.voted) {
                // If the delegate already voted,
                // directly add to the number of votes
                proposals[delegate_.vote].voteCount += sender.weight;
            } else {
                // If the delegate did not vote yet,
                // add to her weight.
                delegate_.weight += sender.weight;
            }
        }
    
        /// Give your vote (including votes delegated to you)
        /// to proposal `proposals[proposal].name`.
        function vote(uint proposal) public {
            Voter storage sender = voters[msg.sender];
            require(!sender.voted);
            sender.voted = true;
            sender.vote = proposal;
    
            // If `proposal` is out of the range of the array,
            // this will throw automatically and revert all
            // changes.
            proposals[proposal].voteCount += sender.weight;
        }
    
        /// @dev Computes the winning proposal taking all
        /// previous votes into account.
        function winningProposal() public view
                returns (uint winningProposal_)
        {
            uint winningVoteCount = 0;
            for (uint p = 0; p < proposals.length; p++) {
                if (proposals[p].voteCount > winningVoteCount) {
                    winningVoteCount = proposals[p].voteCount;
                    winningProposal_ = p;
                }
            }
        }
    
        // Calls winningProposal() function to get the index
        // of the winner contained in the proposals array and then
        // returns the name of the winner
        function winnerName() public view
                returns (bytes32 winnerName_)
        {
            winnerName_ = proposals[winningProposal()].name;
        }
    }

    The example comes from the documention of Solidity.

    Now, this kind of contract is formally defined, so there is no room for different interpretations, and that means there is no reason for litigations around it. It can also be executed by a fair system and it can be executed automatically. These are all good qualities.

    So what are the problems with it? Let’s see.

    What is wrong with the way we currently define “smart contracts”?

    The main issue I see today with smart contracts are the languages used to define those contracts. They are so terribly technical and low level and this is a huge issue.

    Why I say that they are technical?

    • Because we talk about very low level data types, like bytes used to represent strings.
    • Because we use basic for-loops to iterate over collections.
    • Because in general the distance between the intention described in the comments and the implementation that follows them is not trivial at all.

    There are people who would never understand the code and they will have to trust the fact that the comments are correct and they represent exactly the behavior of the corresponding code.

    But why is this really bad? Because contracts are difficult to understand for the people that should use them: lawyers and business men. Given they are so technical what happens is that:

    1. Domain experts (lawyers and business men) need to explain to developers want they want to achieve. This is necessary because only developers have the technical skills to specify the smart contracts using languages like Solidity. Misunderstandings are possible here as domain experts and developers have very different backgrounds and think differently. Even without misunderstanding the process is long and costly. It is long as it take days to communicate between domain experts and developers and later to verify the implementation maps the expectations. It is costly because developers have to be paid for the time they spend to understand what domain experts need and translate it into executable code
    2. The languages used are difficult to work with and errors are very possible. Very low level errors like overflows, buffer being too small or unintended consequences due to the order of actions can be miss while looking at the code
    3. When someone have to decide if to subscribe a contract he or his organization did not write it is very difficult to understand it and there is a risk to miss important conditions

    Consider the fact that in the last months we have read many stories of contract that did not behave as expected and lead to people losing up to hundreds of millions of dollars. Why that happened? Because there were technical subtleties that made this possible. You see, domain experts thought in domain terms “if I pay this I will get this good delivered to me“. However this ideas had to be translated in low level technical instructions and this translation was not always perfect leading to cases in which the result was different from what they expected: what if the payment number is negative? What if an overflow error is caused on purpose? All these questions have to considered because the abstraction is leaky.

    In synthesis the fact the language is so technical prevent domain experts to use it, so that smart contracts cannot be written by the right people. This is explained fairly well on the website of OLE, a DSL to describe smart contracts:

    Building a blockchain means drafting smart contracts wrapped in business models takes 90% of the time. Smart contracts are real contracts. Therefore this work should be done in the conference room. It’s amazing how companies run their business from the server room nowadays, blinded by techno hypes and blockchain evangelization.

    Are natural languages the solution?

    Some people suggest that we should just be able to use natural language to define contracts and then software should just somehow interpret that natural language. I find this naive and I think this is typically the approach that someone without experience in the field of language engineering would suggest.

    An argument for this approach is we are currently accustomed to write contracts in a natural language. I think this is not strictly the case: I think that today lawyers do not use in contracts the same natural language we use in daily discussions. I understand they use terms and formulas that have a very detailed and specific meaning when used in contracts. This meaning could be slightly different from what we attribute them when talking with our friends. Also, many terms we would use in contracts we would never use outside contracts. What I mean is that contracts have their specific language, that is not codified as well and explicitly as we do for formal languages we are used to but that is is anyway different from the common natural language we employ at the bar or at the grocery store. So the idea of using natural language could give us a false sense of security.

    I also think that natural language is imprecise and lead to possible different interpretations. I think we need to use formal and concise languages to define the contracts. We could then derive from that a sort of description or explanation in natural language. This would permit to make possible for more people to understand the contracts but still the work on the contracts itself should be done using a specific language which is formally defined.

    Portability

    Another important aspect is portability: today we are assisting to an explosion of blockchains. Different blockchains could support different languages to define smart contracts and this is an issue because users will have to learn different languages to operate on different blockchains and similar contracts will have to be rewritten for every single blockchian: quite a waste of time.

    The problem is also that blockchain developers do not have typically experience as language designers but as low-level developers instead. They put together languages for smart contracts as an afterthought and the results are very frequently underwhelming. As a language designer I would probably be not the best person in the world to write blockchain technology. The same is true for blockchain developers: they are arguably not great at designing languages.

    What I think would help instead is defining one DSL to define smart contracts and then have translators to multiple target languages. For example, we could create a proper DSL and then a translator that produce the corresponding Solidity code, so that we can have a comprehensible contract that can be executed on the Ethereum blockchain.

    We want one DSL to write contracts and the possibility ot applying it to different blockchains. This would be particularly useful in this phase in which many new blockchain arises: having the possibility to use contracts on those blockchains without having to invest in learning their specific language for contracts would help significantly their adoption.

    Existing alternatives to Solidity

    Let’s focus exclusively on Ethereum and its virtual machine (EVM). There are already alternative languages that can be used instead of Solidity. For example, Pyramid.

    How it looks like? Like this:

    (begin
      (define (factorial n)
        (if (= n 1)
            1
            (* (factorial (- n 1)) n)))
      (factorial 5))

    Is that something easy to understand and carefully designed to look non-scary to non-developers? Well, I guess not.

    Of course the Lisp guys were not left alone and also people who prefers OCaml or Haskell joined the party, so you can write contracts using Haskell syntax:

    main :: IO ()
    main = putStrLn $ codegen prog1
    
    prog1 :: EvmAsm
    prog1 = do
        push1 0x10
        push1 0x20
        add
    

    While I like Haskell, I would hardly say this is the solution we are looking for.

    There are other languages like Flint, Bamboo, and sCrypt that seems to be going in the right direction. However they still look mostly like general purpose languages and not like languages built for a very specific goal, in other words they do not seem Domain Specific Languages.

    An example of what future could look like

    When you want to take a look at how DSLs could look in the future you should keep an eye on what Markus Völter is doing.

    He recently wrote about a prototype of a smart contract development stack (I, II). I fully agree on the vast majority of his considerations and the prototype he is sharing can give you a good ideas of what DSLs can bring to the table.

    At this stage I do not have a prototype of my own to share. However I would like to work with professionals interested in smart contracts to better understand what concepts are emerging in the domain and which patterns we need to elevate to language constructs.

    Even at this early stage it is obvious that we should move away from working with bytes and instead represent directly in the language:

    • The roles of the actors involved
    • Types of assets to be exchanged
    • Decision support: we can have decisions that need to be taken by different kinds of majority (simple, qualified, etc.) with votes of people having different weights depending on their roles or the assets they contributed. The first article from Markus shows how a DSL for decision support could look like.

    Of course once we have the right languages we could focus on the right tooling: simulators, testing support, etc. Tooling is a big part of any Language Engineering project and it is the weapon to deliver most of the advantages. In this specific context some limited form of formal verification could prove to be a huge advantage with respect to the status quo.

    And what about you? Are you using smart contracts? What elements do you think are missing?

    Summary

    There are huge margins to improve the languages currently used to write smart contracts. I think this is something we as a community should do because the right languages would permit to involve different kinds of figures in writing and using these contracts.

    I think that this new possibilities, as many others, is currently just accessible to developers and this is limiting as there are people with different backgrounds and different skillsets who could help using better smart contracts. Also, I think we should work on making new possibilities, like smart contracts, available to a larger group of people. Too many things in this modern world of ours are accessible only to skilled developers. Building the right languages can change that.

    Download the guide with 68 resources on Creating Programming Languages

    Receive the guide to your inbox to read it on all your devices when you have time

    Powered by ConvertKit
     
    Creating a Programming Language

    Learn to Create Programming Languages

    Subscribe to our newsletter to get the FREE email course that teaches you how to create a programming language