Skip to content

Latest commit

 

History

History

pragmas

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Language Syntax Pragmas - Let's Evolve Ruby by Experimenting in a Pragma(tic) Way

pragmas gem / library - language syntax pragmas - turn on the future today or add your own ideas for easy (re)use for everyone - let's evolve the ruby language together by experimenting in the wild in a pragma(tic) way

Usage

The idea with pragmas is to let everyone (yes, you can) add ideas for easy (re)use for everyone -
let's evolve the ruby language syntax together by experimenting in the wild in a pragma(tic) way.
Turn on the future today with pragmas.

By Example

Pattern MatcherEnum Type(Algebraic) Union Data Types (with Variants)Type TypePretty Symbol KeysStruct TypeOption / Maybe / NullableNatural / Positive Integer NumbersStorage / StateAuto Numbered Block Arguments / ParametersDo End-of-Line BlockTyped Data StructuresTyped ArraysPreprocessor Directives

Pattern Matcher

Will replace (transform) lines starting with

| None    =>
| Some(x) =>

to

None: ->()  
Some: ->(x)

Example in the wild:

match Map.find( Current.sender, account_from.allowances ), {
      | None          => { failwith( "Not allowed to spend from", from ) },
      | Some(allowed) => {
        match is_nat(allowed - tokens), {
          | None          => { failwith( "Not enough allowance for transfer", allowed ) },
          | Some(allowed) => {
            if allowed == 0.p
              Map.remove( Current.sender, account_from.allowances )
            else
              Map.add( Current.sender, allowed, account_from.allowances )
            end
          }
        }
      }
    }

turns into:

match Map.find( Current.sender, account_from.allowances ), {
      None: ->()        { failwith( "Not allowed to spend from", from ) },
      Some: ->(allowed) {
        match is_nat(allowed - tokens), {
          None: ->() { failwith( "Not enough allowance for transfer", allowed ) },
          Some: ->(allowed) {
            if allowed == 0.p
              Map.remove( Current.sender, account_from.allowances )
            else
              Map.add( Current.sender, allowed, account_from.allowances )
            end
          }
        }
      }
    }

Enum Type

Will replace (transform) lines starting with enum

enum Color = Red | Green | Blue
enum State = Fundraising | ExpiredRefund | Successful   #or
enum State = Fundraising
           | ExpiredRefund
           | Successful

to

enum :Color, :red, :green, :blue
enum :State, :fundraising, :expired_refund, :successful

Note: For type-safe enums in ruby see s6ruby/enums ».

(Algebraic) Union Data Types (with Variants)

Will replace (transform) lines starting with data

data TravelClass = First | Business | Economy

to

data :TravelClass, :First, :Business, :Economy

Example in the wild:

ticket = First
cost = TravelClass.match ticket, {
         | First    => { 800 },
         | Business => { 500 },
         | Economy  => { 200 }
       }
}

Note: For (algebraic) union data types with (enumerated) variants in ruby see s6ruby/safedata ».

Type Type

Will replace (transform) lines starting with type

type Storage =
type Account =

to

type :Storage,
type :Account,

Example in the wild:

type Account = {
  balance:      Nat,
  allowances:   Map‹Address→Nat› }

type Storage = {
  accounts:     BigMap‹Address→Account›,
  version:      Nat,
  total_supply: Nat,
  decimals:     Nat,
  name:         String,
  symbol:       String,
  owner:        Address }

turns into:

type :Account, {
  balance:      Nat,
  allowances:   Map‹Address→Nat› }

type :Storage, {
  accounts:     BigMap‹Address→Account›,
  version:      Nat,
  total_supply: Nat,
  decimals:     Nat,
  name:         String,
  symbol:       String,
  owner:        Address }

Pretty Symbol Keys

Will remove trailing spaces in symbol keys before the colon (:)

balance    : Nat,
allowances : Map‹Address→Nat›,

turns into:

balance:     Nat,
allowances:  Map‹Address→Nat›,

Example in the wild:

type Account = {
  balance      : Nat,
  allowances   : Map‹Address→Nat› }

type Storage = {
  accounts     : BigMap‹Address→Account›,
  version      : Nat,
  total_supply : Nat,
  decimals     : Nat,
  name         : String,
  symbol       : String,
  owner        : Address }

turns into:

type Account = {
  balance:     Nat,
  allowances:  Map‹Address→Nat› }

type Storage = {
  accounts:     BigMap‹Address→Account›,
  version:      Nat,
  total_supply: Nat,
  decimals:     Nat,
  name:         String,
  symbol:       String,
  owner:        Address }

Struct Type

Will replace (transform) lines starting with struct

struct Voter    = 
struct Proposal =

to

struct :Voter, 
struct :Proposal,

Example in the wild:

struct Voter    = { weight:   0,
                    voted:    false,
                    vote:     0,
                    delegate: Address(0) }

struct Proposal = { vote_count: 0 }

turns into:

struct :Voter,    { weight:   0,
                    voted:    false,
                    vote:     0,
                    delegate: Address(0) }

struct :Proposal, { vote_count: 0 }

Option / Maybe / Nullable

Will replace / unroll the option type shortcut <type>?

Integer?
Game?

to

Option.of(Integer)   # note: same as Option‹Integer›
Option.of(Game)      # note: same as Option‹Game›

Natural / Positive Integer Numbers

Adds the "missing" dot (.) to natural / positive integer numbers

0p
1p

turns into:

0.p
1.p

Storage / State

Will replace (transform) @<id> to storage[:id]

@owner
@greeting

to

storage[:owner]
storage[:greeting]

Auto Numbered Block Arguments / Parameters

Will replace &<num> to the upcoming @<num>

(1..9).each_slice(3).map { &1 + &2 + &3 }
[{name: 'foo'}, {name: 'bar'}].map { &1[:name] }

to

(1..9).each_slice(3).map { @1 + @2 + @3 }
[{name: 'foo'}, {name: 'bar'}].map { @1[:name] }

Do End-of-Line Block

Will replace do: until the end of the line with a {} code block

(1..9).each_slice(3).map do: &1 + &2 + &3
[{name: 'foo'}, {name: 'bar'}].map do: &1[:name]

to

(1..9).each_slice(3).map { &1 + &2 + &3 }
[{name: 'foo'}, {name: 'bar'}].map { &1[:name] }

Typed Data Structures

Will replace <type>‹<value_type>› with <type>.of( <value_type> ) or <type>‹<key_type>→<value_type>› with <type>.of( <key_type>, <value_type> )

Array‹Operation›
Array‹Proposal›×2
Array‹Integer›×9
Hash‹Address→Integer›
Option‹Game›

to

Array.of( Operation )
Array.of( Proposal, 2 ) 
Array.of( Integer, 9 )
Hash.of( Address, Integer )
Option.of( Game )

Note: For type-safe arrays or hash tables in ruby see s6ruby/safestruct ».

Typed Arrays

Will replace / unroll the array shortcut <type>[] or <type>[<num>] or <type>[<id>]

Proposal[]
Proposal[2]
Proposal[ num_proposals ]
Integer[9]

to

Array.of( Proposal )                # note: same as Array‹Proposal›
Array.of( Proposal, 2 )             # note: same as Array‹Proposal›×2
Array.of( Proposal, num_proposals )
Array.of( Integer, 9 )              # note: same as Array‹Integer›×9

Note: For type-safe arrays in ruby see s6ruby/safestruct ».

Preprocessor Directives

Turns preprocessor directives into config hash lookups or predicates / boolean tests

$DEFINED[:bound]
$TRUE[:bound]
$FALSE[:bound]
$PARA[:bound]

results in:

!config[:bound].nil?
config[:bound].true?
config[:bound].false?
config[:bound]

Example in the wild:

def remove( cell, k )
  assert 0 <= cell
  assert cell < @size
  assert 1 <= k
  if $DEFINED[:bound]
    assert k <= $PARA[:bound]
  end
  assert k <= @deck[cell]

  # ...
end

def claim
  # ...
  if $TRUE[:winner_is_last]
    @winner = 3 - @nextPlayer
  else
    @winner = @nextPlayer
  end
end

turns into:

def remove( cell, k )
  assert 0 <= cell
  assert cell < @size
  assert 1 <= k
  if !config[:bound].nil?
    assert k <= config[:bound]
  end
  assert k <= @deck[cell]

  # ...
end

def claim
  # ...
  if config[:winner_is_last].true?
    @winner = 3 - @nextPlayer
  else
    @winner = @nextPlayer
  end
end

Note: For type-safe true? and false? in ruby see s6ruby/safebool ».

And so on and so forth. What's your idea / pragma?

Frequently Asked Questions (F.A.Q.s) and Answers

Q: Are pragmas a new idea?

A: No. (Almost) every programming language has pragma(tic) language flags / directives. See Directive (programming) @ Wikipedia for more.

Q: What about classic "standard" Ruby today?

A: Yes, Ruby has (a few built-in) pragmas too in the form of "magic" comment pragmas / directives. Example:

# encoding:              utf-8
# frozen_string_literal: true
# warn_indent:           true

With Ruby 2.3 and higher, the following are available:

Pragma Comments
encoding: <encoding_name> (defaults to utf-8) or use the coding: shorhand Use any supported encoding, see Encoding.name_list for all (built-in) encoding names.
frozen_string_literal: true|false (defaults tofalse) When enabled, Ruby throws errors when (frozen) strings (literals) are changed "in-place".
warn_indent: true|false (defaults to false) When enabled, and running Ruby with the -w option, throws warnings for code that isn't indented by two spaces.

References

What's a (Language Syntax Preprocessor) Pragma / Directive? See Directive (programming) @ Wikipedia on Pragmas in other languages (including C/C++, Ada, Common Lisp, Turbo Pascal, Perl, Haskell, Python, JavaScript, and more).

License

The pragmas scripts are dedicated to the public domain. Use it as you please with no restrictions whatsoever.

Questions? Comments?

Send your questions and comments to the ruby-talk mailing list. Thanks!