cannam@48: # Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors cannam@48: # Licensed under the MIT License: cannam@48: # cannam@48: # Permission is hereby granted, free of charge, to any person obtaining a copy cannam@48: # of this software and associated documentation files (the "Software"), to deal cannam@48: # in the Software without restriction, including without limitation the rights cannam@48: # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@48: # copies of the Software, and to permit persons to whom the Software is cannam@48: # furnished to do so, subject to the following conditions: cannam@48: # cannam@48: # The above copyright notice and this permission notice shall be included in cannam@48: # all copies or substantial portions of the Software. cannam@48: # cannam@48: # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@48: # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@48: # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@48: # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@48: # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@48: # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@48: # THE SOFTWARE. cannam@48: cannam@48: @0x85150b117366d14b; cannam@48: cannam@48: interface Calculator { cannam@48: # A "simple" mathematical calculator, callable via RPC. cannam@48: # cannam@48: # But, to show off Cap'n Proto, we add some twists: cannam@48: # cannam@48: # - You can use the result from one call as the input to the next cannam@48: # without a network round trip. To accomplish this, evaluate() cannam@48: # returns a `Value` object wrapping the actual numeric value. cannam@48: # This object may be used in a subsequent expression. With cannam@48: # promise pipelining, the Value can actually be used before cannam@48: # the evaluate() call that creates it returns! cannam@48: # cannam@48: # - You can define new functions, and then call them. This again cannam@48: # shows off pipelining, but it also gives the client the cannam@48: # opportunity to define a function on the client side and have cannam@48: # the server call back to it. cannam@48: # cannam@48: # - The basic arithmetic operators are exposed as Functions, and cannam@48: # you have to call getOperator() to obtain them from the server. cannam@48: # This again demonstrates pipelining -- using getOperator() to cannam@48: # get each operator and then using them in evaluate() still cannam@48: # only takes one network round trip. cannam@48: cannam@48: evaluate @0 (expression :Expression) -> (value :Value); cannam@48: # Evaluate the given expression and return the result. The cannam@48: # result is returned wrapped in a Value interface so that you cannam@48: # may pass it back to the server in a pipelined request. To cannam@48: # actually get the numeric value, you must call read() on the cannam@48: # Value -- but again, this can be pipelined so that it incurs cannam@48: # no additional latency. cannam@48: cannam@48: struct Expression { cannam@48: # A numeric expression. cannam@48: cannam@48: union { cannam@48: literal @0 :Float64; cannam@48: # A literal numeric value. cannam@48: cannam@48: previousResult @1 :Value; cannam@48: # A value that was (or, will be) returned by a previous cannam@48: # evaluate(). cannam@48: cannam@48: parameter @2 :UInt32; cannam@48: # A parameter to the function (only valid in function bodies; cannam@48: # see defFunction). cannam@48: cannam@48: call :group { cannam@48: # Call a function on a list of parameters. cannam@48: function @3 :Function; cannam@48: params @4 :List(Expression); cannam@48: } cannam@48: } cannam@48: } cannam@48: cannam@48: interface Value { cannam@48: # Wraps a numeric value in an RPC object. This allows the value cannam@48: # to be used in subsequent evaluate() requests without the client cannam@48: # waiting for the evaluate() that returns the Value to finish. cannam@48: cannam@48: read @0 () -> (value :Float64); cannam@48: # Read back the raw numeric value. cannam@48: } cannam@48: cannam@48: defFunction @1 (paramCount :Int32, body :Expression) cannam@48: -> (func :Function); cannam@48: # Define a function that takes `paramCount` parameters and returns the cannam@48: # evaluation of `body` after substituting these parameters. cannam@48: cannam@48: interface Function { cannam@48: # An algebraic function. Can be called directly, or can be used inside cannam@48: # an Expression. cannam@48: # cannam@48: # A client can create a Function that runs on the server side using cannam@48: # `defFunction()` or `getOperator()`. Alternatively, a client can cannam@48: # implement a Function on the client side and the server will call back cannam@48: # to it. However, a function defined on the client side will require a cannam@48: # network round trip whenever the server needs to call it, whereas cannam@48: # functions defined on the server and then passed back to it are called cannam@48: # locally. cannam@48: cannam@48: call @0 (params :List(Float64)) -> (value :Float64); cannam@48: # Call the function on the given parameters. cannam@48: } cannam@48: cannam@48: getOperator @2 (op :Operator) -> (func :Function); cannam@48: # Get a Function representing an arithmetic operator, which can then be cannam@48: # used in Expressions. cannam@48: cannam@48: enum Operator { cannam@48: add @0; cannam@48: subtract @1; cannam@48: multiply @2; cannam@48: divide @3; cannam@48: } cannam@48: }