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