├── docs ├── .nojekyll ├── css │ ├── common.css │ └── full_list.css ├── frames.html ├── file_list.html ├── top-level-namespace.html ├── Ruff │ ├── Throws.html │ ├── Standard.html │ ├── Throws │ │ ├── Eff.html │ │ └── Resend.html │ └── Standard │ │ ├── CurrentTime.html │ │ ├── Defer.html │ │ └── MeasureTime.html ├── Util.html ├── Util │ ├── ADT.html │ ├── Ref.html │ └── FnStack.html ├── js │ ├── full_list.js │ └── app.js ├── class_list.html ├── Ruff.html ├── index.html ├── _index.html └── file.README.html ├── version ├── lib ├── ruff │ ├── version.rb │ ├── version.gen.sh │ ├── standard.rb │ ├── standard │ │ ├── util.rb │ │ ├── delim_ctrl.rb │ │ ├── current_time.rb │ │ ├── defer.rb │ │ ├── measure_time.rb │ │ ├── state.rb │ │ └── async.rb │ ├── objects.rb │ ├── effect.rb │ └── handler.rb └── ruff.rb ├── Gemfile ├── .envrc ├── .solargraph.yml ├── .github ├── dependabot.yml └── workflows │ ├── test.yml │ ├── update-nix.yml │ └── hook-dependabot.yml ├── examples └── example.rb ├── spec ├── spec_helper.rb ├── ruff_spec.rb └── ruff │ ├── effect_spec.rb │ ├── standard_spec.rb │ └── handler_spec.rb ├── .rubocop.yml ├── LICENSE ├── .gitignore ├── ruff.gemspec ├── flake.nix ├── Rakefile ├── README.md ├── Gemfile.lock ├── CLAUDE.md └── flake.lock /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | 2.1.0 2 | -------------------------------------------------------------------------------- /docs/css/common.css: -------------------------------------------------------------------------------- 1 | /* Override this file with custom rules */ -------------------------------------------------------------------------------- /lib/ruff/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ruff 4 | VERSION = '2.1.0' 5 | end 6 | -------------------------------------------------------------------------------- /lib/ruff/version.gen.sh: -------------------------------------------------------------------------------- 1 | cat < 2 | 3 | 4 | 5 | Ruff 2.1.0 Documentation 6 | 7 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /examples/example.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'ruff' 4 | 5 | Double = Ruff.instance 6 | Triple = Ruff.instance 7 | Log = Ruff.instance 8 | 9 | h1 = Ruff.handler 10 | .on(Double) { |k, v| k[v * 2] } 11 | .on(Triple) { |k, v| k[v * 3] } 12 | 13 | h2 = Ruff.handler.on(Log) do |k, msg| 14 | puts "logger: #{msg}" 15 | k[] 16 | end 17 | 18 | h1.run do 19 | h2.run do 20 | v = Double.perform 2 21 | Log.perform(v + 2) 22 | puts Triple.perform 3 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/setup' 4 | require 'rspec' 5 | require 'ruff' 6 | require 'ruff/standard' 7 | 8 | RSpec.configure do |config| 9 | # Enable flags like --only-failures and --next-failure 10 | config.example_status_persistence_file_path = '.rspec_status' 11 | 12 | # Disable RSpec exposing methods globally on `Module` and `main` 13 | config.disable_monkey_patching! 14 | 15 | config.expect_with :rspec do |c| 16 | c.syntax = :expect 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/ruff_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe Ruff do 6 | it 'has a version number' do 7 | expect(Ruff::VERSION).not_to be_nil 8 | end 9 | 10 | describe '.instance' do 11 | it 'is an alias for Effect.new' do 12 | expect(described_class.instance).to be_an_instance_of(Ruff::Effect) 13 | end 14 | end 15 | 16 | describe '.handler' do 17 | it 'is an alias for Handler.new' do 18 | expect(described_class.handler).to be_an_instance_of(Ruff::Handler) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | types: 8 | - opened 9 | - synchronize 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: DeterminateSystems/determinate-nix-action@v3 17 | with: 18 | extra-conf: | 19 | cores = 0 20 | - uses: DeterminateSystems/magic-nix-cache-action@v13 21 | - uses: nicknovitski/nix-develop@v1 22 | - run: | 23 | rake rubocop 24 | rake spec 25 | actionlint 26 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - rubocop-performance 3 | - rubocop-rspec 4 | - rubocop-rake 5 | 6 | AllCops: 7 | NewCops: enable 8 | 9 | Layout/LineLength: 10 | Max: 120 11 | 12 | Metrics/MethodLength: 13 | Max: 40 14 | 15 | Naming/MethodParameterName: 16 | MinNameLength: 1 17 | 18 | Style/MultilineBlockChain: 19 | Enabled: false 20 | 21 | Style/Documentation: 22 | Enabled: false 23 | 24 | Style/ClassAndModuleChildren: 25 | Enabled: false 26 | 27 | Metrics/BlockLength: 28 | Exclude: 29 | - 'spec/**/*.rb' 30 | 31 | RSpec/ExampleLength: 32 | Enabled: false 33 | 34 | RSpec/MultipleExpectations: 35 | Enabled: false 36 | 37 | Gemspec/DevelopmentDependencies: 38 | Enabled: false 39 | -------------------------------------------------------------------------------- /lib/ruff.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ruff 4 | class << self 5 | require 'ruff/version' 6 | require 'ruff/objects' 7 | require 'ruff/effect' 8 | require 'ruff/handler' 9 | require 'securerandom' 10 | 11 | # is alias for `Effect.new` 12 | # @see Effect.initialize Effect.initialize 13 | # 14 | # @example 15 | # Log = Ruff.instance #==> Ruff::Effect.new 16 | def instance 17 | Effect.new 18 | end 19 | 20 | # is alias for `Handler.new` 21 | # @see Handler.initialize Handler.initialize 22 | # 23 | # @example 24 | # log_handler = Ruff.handler.on(Log){|msg, k| 25 | # puts "Logger: #{msg}" 26 | # k[] 27 | # } 28 | def handler 29 | Handler.new 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/ruff/standard.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # `Ruff::Standard` provides several pre-defined effect handlers modules. 4 | # Each module provides `Instance` class to instantiate and handle the indivisual effect instances. 5 | # @example 6 | # include Ruff::Standard 7 | # 8 | # state1 = State::Instance.new 9 | # state2 = State::Instance.new 10 | # 11 | # state1.with_init(3) { 12 | # state2.with_init(4) { 13 | # state2.modify {|s| s + state1.get } 14 | # 15 | # puts state1.get #==> 3 16 | # puts state2.get #==> 7 17 | # }} 18 | # 19 | module Ruff::Standard end 20 | 21 | require 'ruff' 22 | require 'ruff/standard/current_time' 23 | require 'ruff/standard/measure_time' 24 | require 'ruff/standard/defer' 25 | require 'ruff/standard/state' 26 | require 'ruff/standard/async' 27 | require 'ruff/standard/delim_ctrl' 28 | -------------------------------------------------------------------------------- /lib/ruff/standard/util.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Util end 4 | 5 | class Util::FnStack 6 | def initialize 7 | @queue = [] 8 | end 9 | 10 | def enqueue(fn) 11 | @queue.push fn 12 | end 13 | 14 | def dequeue 15 | hd = @queue.pop 16 | hd[] unless hd.nil? 17 | end 18 | 19 | def cons(hd) 20 | queue_ = @queue.dup 21 | queue_.push hd 22 | end 23 | end 24 | 25 | class Util::Ref 26 | def initialize(v) 27 | @v = v 28 | end 29 | 30 | def get 31 | @v 32 | end 33 | 34 | def set(v_) 35 | @v = v_ 36 | end 37 | end 38 | 39 | module Util::ADT 40 | def create 41 | Class.new do 42 | def initialize(v) 43 | @value = v 44 | end 45 | 46 | def get 47 | @value 48 | end 49 | end 50 | end 51 | 52 | module_function :create 53 | end 54 | -------------------------------------------------------------------------------- /.github/workflows/update-nix.yml: -------------------------------------------------------------------------------- 1 | name: update-flake-lock 2 | on: 3 | workflow_dispatch: # allows manual triggering 4 | schedule: 5 | - cron: '0 0 1 * *' # runs every monyh 1st 6 | 7 | permissions: 8 | contents: write 9 | pull-requests: write 10 | 11 | jobs: 12 | lockfile: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: DeterminateSystems/determinate-nix-action@v3 17 | with: 18 | extra-conf: | 19 | cores = 0 20 | - uses: DeterminateSystems/magic-nix-cache-action@v13 21 | - name: Update flake.lock 22 | uses: DeterminateSystems/update-flake-lock@v27 23 | with: 24 | pr-title: "Update flake.lock" # Title of PR to be created 25 | pr-labels: | # Labels to be set on the PR 26 | dependencies 27 | automated 28 | -------------------------------------------------------------------------------- /lib/ruff/objects.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ruff::Throws end 4 | 5 | # `Eff` is internal object. 6 | # 7 | # They make effects encapsulate with ID and arguments to be sent to the handler. 8 | class Ruff::Throws::Eff 9 | # makes the object unique. 10 | attr_reader :id 11 | 12 | # passes to a handler which can catch the effect. 13 | attr_reader :args 14 | 15 | # creates a new object with `id` and `args`. 16 | def initialize(id, args) 17 | @id = id 18 | @args = args 19 | end 20 | end 21 | 22 | # `Resend` is internal object like `Eff`. 23 | # 24 | # It is used when an effect is unable to be handled and should be thrown to the outer handler. 25 | class Ruff::Throws::Resend 26 | # is abstracted effect (such as `Eff` or (re)thrown `Resend`). 27 | attr_reader :eff 28 | 29 | # is a continuation of `eff` thrown context. 30 | attr_reader :k 31 | 32 | # creates a new object with `eff` and `k`. 33 | def initialize(eff, k) 34 | @eff = eff 35 | @k = k 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /.github/workflows/hook-dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Run bundix on dependencies-labeled PRs 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | - synchronize 7 | 8 | permissions: 9 | contents: read 10 | pull-requests: write 11 | 12 | jobs: 13 | followup-bundix: 14 | if: | 15 | github.actor == 'dependabot[bot]' 16 | runs-on: ubuntu-latest 17 | env: 18 | head_ref: ${{ github.event.pull_request.head.ref }} 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | ref: ${{ env.head_ref }} 23 | - uses: DeterminateSystems/nix-installer-action@v19 24 | with: 25 | extra-conf: | 26 | cores = 0 27 | - uses: DeterminateSystems/magic-nix-cache-action@v13 28 | - run: nix run '.#bundix' 29 | - uses: EndBug/add-and-commit@v9 30 | with: 31 | author_name: 'github-actions[bot]' 32 | author_email: 'github-actions[bot]@users.noreply.github.com' 33 | message: 'Update gemset.nix via bundix' 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Nymphium 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/ruff/standard/delim_ctrl.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # `DelimCtrl` provides one-shot delimited control operators, `shift` and `reset`. 4 | # @example 5 | # DelimCtrl.reset { 6 | # puts "hello" 7 | # DelimCtrl.shift { |k| 8 | # k.call 9 | # puts "!" 10 | # } 11 | # 12 | # puts "world" 13 | # } 14 | # # ==> 15 | # # hello 16 | # # world 17 | # # ! 18 | module Ruff::Standard::DelimCtrl 19 | # prompt stack 20 | @stack = [] 21 | 22 | # delimits a continuation 23 | # @param [Proc<(), A>] th 24 | # is a thunk. In this thunk `shift` captures a continuation delimited with the thunk. 25 | def reset(&th) 26 | eff = Ruff::Effect.new 27 | @stack.push eff 28 | 29 | ret = Ruff.handler 30 | .on(eff) do |k, f| 31 | f.call(k) 32 | end 33 | .run(&th) 34 | 35 | @stack.pop 36 | ret 37 | end 38 | 39 | # captures a continuation. 40 | # @param [Proc, A/B>] k 41 | # is a continuation. 42 | def shift(&k) 43 | # fetch nearmost prompt 44 | top = @stack.last 45 | top.perform(k) 46 | end 47 | 48 | module_function :reset, :shift 49 | end 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/ruby 3 | # Edit at https://www.gitignore.io/?templates=ruby 4 | 5 | ### Ruby ### 6 | *.gem 7 | *.rbc 8 | /.config 9 | /coverage/ 10 | /InstalledFiles 11 | /pkg/ 12 | /spec/reports/ 13 | /spec/examples.txt 14 | /test/tmp/ 15 | /test/version_tmp/ 16 | /tmp/ 17 | 18 | # Used by dotenv library to load environment variables. 19 | # .env 20 | 21 | # Ignore Byebug command history file. 22 | .byebug_history 23 | 24 | ## Specific to RubyMotion: 25 | .dat* 26 | .repl_history 27 | build/ 28 | *.bridgesupport 29 | build-iPhoneOS/ 30 | build-iPhoneSimulator/ 31 | 32 | ## Specific to RubyMotion (use of CocoaPods): 33 | # 34 | # We recommend against adding the Pods directory to your .gitignore. However 35 | # you should judge for yourself, the pros and cons are mentioned at: 36 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 37 | # vendor/Pods/ 38 | 39 | ## Documentation cache and generated files: 40 | /.yardoc/ 41 | /_yardoc/ 42 | /doc/ 43 | /rdoc/ 44 | 45 | ## Environment normalization: 46 | /.bundle/ 47 | /vendor/bundle 48 | /lib/bundler/man/ 49 | 50 | .ruby-version 51 | .ruby-gemset 52 | .rspec_status 53 | 54 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 55 | .rvmrc 56 | 57 | # End of https://www.gitignore.io/api/ruby 58 | /.direnv 59 | -------------------------------------------------------------------------------- /ruff.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path('lib', __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require 'ruff/version' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = 'ruff' 9 | spec.version = Ruff::VERSION 10 | spec.authors = ['Nymphium'] 11 | spec.email = ['s1311350@gmail.com'] 12 | 13 | spec.summary = 'ONE-SHOT Algebraic Effects for Ruby!' 14 | spec.description = 'ONE-SHOT Algebraic Effects for Ruby!' 15 | spec.homepage = 'https://github.com/Nymphium/ruff' 16 | spec.license = 'MIT' 17 | 18 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 19 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 20 | end 21 | spec.require_paths = ['lib'] 22 | 23 | spec.required_ruby_version = '>= 2.7.0' # Assuming Ruby 2.7 or higher 24 | 25 | spec.metadata = { 26 | 'documentation_uri' => 'https://nymphium.github.io/ruff', 27 | 'rubygems_mfa_required' => 'true' 28 | } 29 | 30 | spec.add_development_dependency 'rake' 31 | spec.add_development_dependency 'redcarpet' 32 | spec.add_development_dependency 'rspec' 33 | spec.add_development_dependency 'rubocop' 34 | spec.add_development_dependency 'rubocop-performance' 35 | spec.add_development_dependency 'rubocop-rake' 36 | spec.add_development_dependency 'rubocop-rspec' 37 | spec.add_development_dependency 'ruby-lsp' 38 | spec.add_development_dependency 'yard' 39 | end 40 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/*"; 4 | flake-utils.url = "github:numtide/flake-utils"; 5 | bundix = { 6 | url = "github:inscapist/bundix/main"; 7 | inputs.nixpkgs.follows = "nixpkgs"; 8 | }; 9 | ruby-nix = { 10 | url = "github:inscapist/ruby-nix"; 11 | inputs.nixpkgs.follows = "nixpkgs"; 12 | }; 13 | }; 14 | 15 | outputs = 16 | { 17 | nixpkgs, 18 | flake-utils, 19 | ruby-nix, 20 | bundix, 21 | ... 22 | }: 23 | flake-utils.lib.eachDefaultSystem ( 24 | system: 25 | let 26 | pkgs = import nixpkgs { 27 | inherit system; 28 | }; 29 | initRuby = pkgs.ruby; 30 | rubyNix = (ruby-nix.lib pkgs) { 31 | ruby = initRuby; 32 | gemset = ./gemset.nix; 33 | }; 34 | bundix' = pkgs.callPackage bundix { 35 | ruby = initRuby; 36 | }; 37 | 38 | formatter = pkgs.nixfmt-rfc-style; 39 | 40 | devShells.default = pkgs.mkShellNoCC { 41 | packages = [ 42 | rubyNix.ruby 43 | rubyNix.env 44 | 45 | pkgs.actionlint 46 | 47 | pkgs.nil 48 | formatter 49 | ]; 50 | }; 51 | in 52 | { 53 | legacyPackages = pkgs; 54 | apps.bundix = { 55 | program = "${bundix'}/bin/bundix"; 56 | type = "app"; 57 | }; 58 | 59 | inherit formatter devShells; 60 | } 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /lib/ruff/effect.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This class provides an effect instance. 4 | class Ruff::Effect 5 | # Each instance must be unique so they have unique id with an annonymous class instance. 6 | # 7 | # The class instance may have subtyping relation. 8 | # @see << 9 | attr_reader :id 10 | 11 | # instaciates an effect setting `id`. 12 | # @return [Effect] 13 | # @example 14 | # Log = Effect.new #==> it _might_ be Effect 15 | def initialize 16 | @id = Class.new.new 17 | end 18 | 19 | # instanciates an effect, which has an relation `self <: parent` 20 | # from the subtyping of `id` object. 21 | # 22 | # @param [Effect] parent 23 | # @return [Effect] with an relation `it <: parent` 24 | # 25 | # @example 26 | # Exception = Ruff::Effect.new 27 | # RuntimeException = Ruff::Effect << Exception 28 | # 29 | # Ruff::Handler.new 30 | # .on(Exception){ 31 | # puts "catch" 32 | # } 33 | # .run { 34 | # RuntimeException.perform 35 | # } 36 | # # ==> prints "catch" 37 | def self.<<(parent) 38 | inst = new 39 | parent_id = parent.instance_variable_get(:@id) 40 | inst.instance_variable_set(:@id, (Class.new parent_id.class).new) 41 | inst 42 | end 43 | 44 | # sends an effect ID and its arguments to a nearmost handler. 45 | # 46 | # @param [Arg] a of the object `Effect` 47 | # @return [Ret] of the object `Effect` 48 | # 49 | # @example 50 | # Log.perform "hello" 51 | def perform(*a) 52 | Fiber.yield Ruff::Throws::Eff.new(@id, a) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /docs/file_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | File List 19 | 20 | 21 | 22 |
23 |
24 |

File List

25 |
26 | 27 | 28 | Classes 29 | 30 | 31 | 32 | Methods 33 | 34 | 35 | 36 | Files 37 | 38 | 39 |
40 | 41 | 42 |
43 | 44 |
    45 | 46 | 47 |
  • 48 | 49 |
  • 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /lib/ruff/standard/current_time.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # `CurrentTime` provides an effect `CurrentTime.eff` and the implementation returning `Time.now` . 4 | # 5 | # The module has an instance of `Instance` and provides its methods as module method. 6 | # @see Standard::CurrentTime::Instance 7 | module Ruff::Standard::CurrentTime 8 | class Instance 9 | # makes a new instance. 10 | def initialize 11 | @eff = Ruff.instance 12 | @handler = Ruff.handler.on(@eff) { |k| k[Time.now] } 13 | end 14 | 15 | # is a smart method to invoke the effect operation. 16 | # @return [Time] 17 | # with `with` , returns `Time.now` 18 | def get 19 | @eff.perform 20 | end 21 | 22 | # is a handler to interpret the effect invokation as requesting the current time. 23 | # This handler receives the *request* and returns current time. 24 | # 25 | # @param [Proc<(), A!{CurrentTime.eff, e}>] th 26 | # is a thunk returning `A` with the possibility to invoke effects, 27 | # including `CurrentTime.eff` . 28 | # 29 | # @return [A!{e}] 30 | # returns `A` , without modification by value handler. 31 | # But it still has the possibility to invoke effects(`e`). 32 | def with(&th) 33 | @handler.run(&th) 34 | end 35 | 36 | # You can reimplement the handler using this effect instance. 37 | attr_reader :eff 38 | end 39 | 40 | # --- 41 | @inst = Instance.new 42 | @eff = @inst.eff 43 | 44 | # @see Ruff::Standard::CurrentTime::Instance#get 45 | def get 46 | @inst.get 47 | end 48 | 49 | # @see Ruff::Standard::CurrentTime::Instance#with 50 | def with(&th) 51 | @inst.with(&th) 52 | end 53 | 54 | module_function :get, :with 55 | 56 | # @see Ruff::Standard::CurrentTime::Instance#eff 57 | attr_reader :eff 58 | end 59 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/setup' 4 | Bundler.require 5 | require 'rspec/core/rake_task' 6 | require 'yard' 7 | require 'ruff/version' 8 | require 'rubocop/rake_task' 9 | 10 | RuboCop::RakeTask.new 11 | 12 | YARDOPTS = [ 13 | '--output-dir=docs', 14 | "--title=Ruff #{Ruff::VERSION} Documentation", 15 | '--markup-provider=redcarpet', 16 | '--markup=markdown', 17 | '--charset=utf-8', 18 | '--no-private' 19 | ].freeze 20 | 21 | require 'rspec/core' 22 | 23 | RSpec::Core::RakeTask.new(:spec) do |t| 24 | t.pattern = './spec/**/*_spec.rb' 25 | t.rspec_opts = ['--format', 'documentation'] 26 | end 27 | 28 | YARD::Rake::YardocTask.new do |doc| 29 | doc.name = 'doc' 30 | 31 | YARDOPTS.each { |opt| doc.options << opt } 32 | 33 | doc.files = FileList.new('lib/**/*.rb').exclude('**/util.rb') 34 | end 35 | 36 | def handle_minor_and_patch_update(newv, v) 37 | case newv[1] 38 | when '-' 39 | newv[1] = v[1] 40 | newv[2] = v[2].to_i + 1 if newv[2] == '+' 41 | when '+' 42 | newv[1] = v[1].to_i + 1 43 | newv[2] = 0 44 | else 45 | newv[2] = 0 46 | end 47 | end 48 | 49 | def handle_major_update(newv, v) 50 | newv[0] = v[0].to_i + 1 51 | newv[1] = 0 52 | newv[2] = 0 53 | end 54 | 55 | def update_version_parts(newv, v) 56 | case newv[0] 57 | when '-' 58 | newv[0] = v[0] 59 | handle_minor_and_patch_update(newv, v) 60 | when '+' 61 | handle_major_update(newv, v) 62 | end 63 | end 64 | 65 | desc 'new version' 66 | task(:newver, %i[major minor patch]) do |_, args| 67 | f = File.open('version', 'r+') 68 | v = f.read.gsub(/[^a-zA-Z0-9\-_.]/, '').split('.') 69 | newv = args.to_a.dup 70 | 71 | begin 72 | update_version_parts(newv, v) 73 | rescue StandardError => e 74 | puts e 75 | end 76 | 77 | p "#{v.join '.'} => #{newv.join '.'}" 78 | f.seek(0, IO::SEEK_SET) 79 | f.write newv.join '.' 80 | f.close 81 | 82 | sh 'bash lib/ruff/version.gen.sh > lib/ruff/version.rb' 83 | end 84 | 85 | task default: :spec 86 | -------------------------------------------------------------------------------- /lib/ruff/standard/defer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # `Defer` provides effects `Defer.eff` , 4 | # and the implementation to defer procedures . 5 | # 6 | # The module has an instance of `Instance` and provides its methods as module method. 7 | # @see Standard::Defer::Instance 8 | module Ruff::Standard::Defer 9 | class Instance 10 | # makes a new instance. 11 | def initialize 12 | @eff = Ruff.instance 13 | end 14 | 15 | # is a smart method to invoke the effect operation. 16 | # @param [Proc<(), ()>] prc 17 | # is deferred, or "registerred", and called on the end of computation. 18 | # @return [()] 19 | def register(&prc) 20 | @eff.perform prc 21 | end 22 | 23 | # is a handler to interpret the effect invocation as registering a procedure. 24 | # Registerred procedures are run on the end of computation, by value handler. 25 | # 26 | # @param [Proc<(), A>!{Defer.eff, e}] th 27 | # is a thunk returning `A` with the possibility to invoke effects, including `Defer.eff` . 28 | # 29 | # @return [A!{e}] 30 | # returns `A` , without modification by value handler. 31 | # But it still has the possibility to invoke effects(`e`). 32 | def with(&th) 33 | # This is a stack to store deferred procedures. 34 | procs = [] 35 | 36 | # The handler *closes* `procs` variable so it should be created every time. 37 | Ruff.handler 38 | .on(@eff) do |k, prc| 39 | procs << prc 40 | k[] 41 | end 42 | .to do |v| 43 | # Like Go's defer functions, it crashes the thunk by reversed order. 44 | procs.reverse_each(&:[]) 45 | 46 | v 47 | end 48 | .run(&th) 49 | end 50 | 51 | # You can reimplement the handler using this effect instance. 52 | attr_reader :eff 53 | end 54 | 55 | # --- 56 | @inst = Instance.new 57 | @eff = @inst.eff 58 | 59 | # @see Ruff::Standard::Defer::Instance#register 60 | def register(&prc) 61 | @inst.register(&prc) 62 | end 63 | 64 | # @see Ruff::Standard::Defer::Instance#with 65 | def with(&th) 66 | @inst.with(&th) 67 | end 68 | 69 | module_function :register, :with 70 | 71 | # @see Ruff::Standard::Defer::Instance#eff 72 | attr_reader :eff 73 | end 74 | -------------------------------------------------------------------------------- /lib/ruff/standard/measure_time.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # `MeasureTime` provides effects `MeasureTime.eff` , 4 | # and the implementation to measure and report execution time . 5 | # 6 | # The module has an instance of `Instance` and provides its methods as module method. 7 | # @see Standard::MeasureTime::Instance 8 | # 9 | # @example 10 | # MeasureTime.with { 11 | # MeasureTime.measure 'one' 12 | # sleep 1 13 | # MeasureTime.measure 'two' 14 | # sleep 0.1 15 | # 16 | # return 0 17 | # } 18 | # #==> [0, {:label=>"two", :time=>0.1}, {:label=>"one", :time=>1.1}] 19 | module Ruff::Standard::MeasureTime 20 | class Instance 21 | # makes a new instance. 22 | def initialize 23 | @eff = Ruff.instance 24 | @handler = Ruff.handler 25 | @handler.on(@eff) do |k, label| 26 | t1 = Time.now 27 | result = k[] 28 | t2 = Time.now 29 | result + [{ label: label, time: t2 - t1 }] 30 | end 31 | @handler.to { |x| [x] } 32 | end 33 | 34 | # is a smart method to invoke the effect operation. 35 | # @param [string] label 36 | # is the label of the measurement. 37 | # @return [()] 38 | def measure(label) 39 | @eff.perform(label) 40 | end 41 | 42 | # is a handler to interpret the effect invocation as measuring computation time. 43 | # 44 | # @param [Proc<(), A!{MeasureTime.eff, e}>] th 45 | # is a thunk returning `A` with the possibility to invoke effects, 46 | # including `MeasureTime.eff` . 47 | # 48 | # @return [[A, ...{ label: string, time: float }]!{e}] 49 | # returns list. the first is the result `A`, and the rest is the measurement results. 50 | # It still has the possibility to invoke effects(`e`). 51 | def with(&th) 52 | @handler.run(&th) 53 | end 54 | 55 | # You can reimplement the handler using this effect instance. 56 | attr_reader :eff 57 | end 58 | 59 | # --- 60 | @inst = Instance.new 61 | @eff = @inst.eff 62 | 63 | # @see Ruff::Standard::MeasureTime::Instance#measure 64 | def measure(label) 65 | @inst.measure(label) 66 | end 67 | 68 | # @see Ruff::Standard::MeasureTime::Instance#with 69 | def with(&th) 70 | @inst.with(&th) 71 | end 72 | 73 | module_function :measure, :with 74 | 75 | # @see Ruff::Standard::MeasureTime::Instance#eff 76 | attr_reader :eff 77 | end 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ruff 2 | == 3 | 4 | [![Gem Version](https://badge.fury.io/rb/ruff.svg)](https://badge.fury.io/rb/ruff) 5 | 6 | - [source](https://github.com/Nymphium/ruff) 7 | - [documentation](https://nymphium.github.io/ruff) 8 | 9 | ONE-SHOT Algebraic Effects Library for Ruby! 10 | 11 | ```ruby 12 | require "ruff" 13 | 14 | Double = Ruff.instance 15 | Triple = Ruff.instance 16 | Log = Ruff.instance 17 | 18 | h1 = Ruff.handler 19 | .on(Double){|k, v| k[v * 2] } 20 | .on(Triple){|k, v| k[v * 3] } 21 | 22 | h2 = Ruff.handler.on(Log){|k, msg| 23 | k[] 24 | puts "logger: #{msg}" 25 | } 26 | 27 | h1.run{ 28 | h2.run{ 29 | v = Double.perform 2 30 | Log.perform (v + 2) 31 | puts Triple.perform 3 32 | } 33 | } 34 | # ==> prints 35 | # 9 36 | # logger: 6 37 | ``` 38 | 39 | # Feature 40 | ## ***One-shot*** algebraic effects 41 | You can access the delimited continuation which can run only once. 42 | Even the limitation exists, you can write powerful control flow manipulation, like async/await, call1cc. 43 | 44 | We have an formal definition for the implementation, by showing a conversion from algebraic effects and handlers to asymmetric coroutines. 45 | See [here](https://nymphium.github.io/2018/12/09/asymmetric-coroutines%E3%81%AB%E3%82%88%E3%82%8Boneshot-algebraic-effects%E3%81%AE%E5%AE%9F%E8%A3%85.html) (in Japanese). 46 | 47 | ## Subtyping on effects 48 | You can define an *sub* effect for another effect. 49 | It enables to make an effect hierarchy, such as `Exception`s in Java. 50 | This implementation is based on a calculus λσ<: [(Description (in Japanese))](https://nymphium.github.io/2019/12/22/effsub.html). 51 | 52 | # Pre-defined effect and handlers 53 | We provide some ready-to-use effect and handlers. 54 | You can use quickly powerful control flows. 55 | 56 | - [`Ruff::Standard::State`](https://nymphium.github.io/ruff/Ruff/Standard/State.html) 57 | - [`Ruff::Standard::Defer`](https://nymphium.github.io/ruff/Ruff/Standard/Defer.html) 58 | - [`Ruff::Standard::CurrentTime`](https://nymphium.github.io/ruff/Ruff/Standard/CurrentTime.html) 59 | - [`Ruff::Standard::MeasureTime`](https://nymphium.github.io/ruff/Ruff/Standard/MeasureTime.html) 60 | - [`Ruff::Standard::Async`](https://nymphium.github.io/ruff/Ruff/Standard/Async.html) 61 | - [`Ruff::Standard::Call1cc`](https://nymphium.github.io/ruff/Ruff/Standard/Call1cc.html) 62 | 63 | # LICENSE 64 | [MIT](/LICENSE) 65 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | ruff (2.1.0) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | ast (2.4.3) 10 | diff-lcs (1.6.2) 11 | json (2.12.2) 12 | language_server-protocol (3.17.0.5) 13 | lint_roller (1.1.0) 14 | logger (1.7.0) 15 | parallel (1.27.0) 16 | parser (3.3.8.0) 17 | ast (~> 2.4.1) 18 | racc 19 | prism (1.4.0) 20 | racc (1.8.1) 21 | rainbow (3.1.1) 22 | rake (13.3.0) 23 | rbs (3.9.4) 24 | logger 25 | redcarpet (3.6.1) 26 | regexp_parser (2.10.0) 27 | rspec (3.13.1) 28 | rspec-core (~> 3.13.0) 29 | rspec-expectations (~> 3.13.0) 30 | rspec-mocks (~> 3.13.0) 31 | rspec-core (3.13.5) 32 | rspec-support (~> 3.13.0) 33 | rspec-expectations (3.13.5) 34 | diff-lcs (>= 1.2.0, < 2.0) 35 | rspec-support (~> 3.13.0) 36 | rspec-mocks (3.13.5) 37 | diff-lcs (>= 1.2.0, < 2.0) 38 | rspec-support (~> 3.13.0) 39 | rspec-support (3.13.4) 40 | rubocop (1.77.0) 41 | json (~> 2.3) 42 | language_server-protocol (~> 3.17.0.2) 43 | lint_roller (~> 1.1.0) 44 | parallel (~> 1.10) 45 | parser (>= 3.3.0.2) 46 | rainbow (>= 2.2.2, < 4.0) 47 | regexp_parser (>= 2.9.3, < 3.0) 48 | rubocop-ast (>= 1.45.1, < 2.0) 49 | ruby-progressbar (~> 1.7) 50 | unicode-display_width (>= 2.4.0, < 4.0) 51 | rubocop-ast (1.45.1) 52 | parser (>= 3.3.7.2) 53 | prism (~> 1.4) 54 | rubocop-performance (1.25.0) 55 | lint_roller (~> 1.1) 56 | rubocop (>= 1.75.0, < 2.0) 57 | rubocop-ast (>= 1.38.0, < 2.0) 58 | rubocop-rake (0.7.1) 59 | lint_roller (~> 1.1) 60 | rubocop (>= 1.72.1) 61 | rubocop-rspec (3.6.0) 62 | lint_roller (~> 1.1) 63 | rubocop (~> 1.72, >= 1.72.1) 64 | ruby-lsp (0.24.2) 65 | language_server-protocol (~> 3.17.0) 66 | prism (>= 1.2, < 2.0) 67 | rbs (>= 3, < 5) 68 | sorbet-runtime (>= 0.5.10782) 69 | ruby-progressbar (1.13.0) 70 | sorbet-runtime (0.5.12219) 71 | unicode-display_width (3.1.4) 72 | unicode-emoji (~> 4.0, >= 4.0.4) 73 | unicode-emoji (4.0.4) 74 | yard (0.9.37) 75 | 76 | PLATFORMS 77 | arm64-darwin-24 78 | ruby 79 | 80 | DEPENDENCIES 81 | rake 82 | redcarpet 83 | rspec 84 | rubocop 85 | rubocop-performance 86 | rubocop-rake 87 | rubocop-rspec 88 | ruby-lsp 89 | ruff! 90 | yard 91 | 92 | BUNDLED WITH 93 | 2.5.22 94 | -------------------------------------------------------------------------------- /docs/top-level-namespace.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Top Level Namespace 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Top Level Namespace 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 | 81 |

Defined Under Namespace

82 |

83 | 84 | 85 | Modules: Ruff 86 | 87 | 88 | 89 | 90 |

91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
101 | 102 | 107 | 108 |
109 | 110 | -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | # Ruff 2 | 3 | ## Project Description 4 | 5 | Ruff is a Ruby library that provides an implementation of one-shot algebraic effect handlers. Algebraic effect handlers are a way to implement local control flow, which can be used to create powerful abstractions such as async/await, generators, and coroutines. Ruff's effects are "one-shot," meaning that a delimited continuation can be resumed at most once. 6 | 7 | The library also features subtyping on effects, allowing for the creation of effect hierarchies. Several pre-defined effects and handlers are included, such as `State`, `Defer`, `Async`, and `Call1cc`. 8 | 9 | ## Development 10 | 11 | This project uses [Nix Flakes](https://nixos.wiki/wiki/Flakes) and [direnv](https://direnv.net/) to provide a reproducible development environment. 12 | 13 | If you are a Nix user and have `direnv` installed, you can simply run `direnv allow` in the project root. This will automatically install all the required dependencies, including Ruby and the necessary gems, into a sandboxed environment. You will not need to run `bundle install` separately. 14 | 15 | For non-Nix users, you can follow the standard Ruby development setup: 16 | 17 | 1. **Clone the repository:** 18 | ```bash 19 | git clone https://github.com/Nymphium/ruff.git 20 | cd ruff 21 | ``` 22 | 23 | 2. **Install dependencies:** 24 | ```bash 25 | bundle install 26 | ``` 27 | 28 | 3. **Run the tests:** 29 | ```bash 30 | bundle exec rspec 31 | ``` 32 | 33 | ## Instructions for LLMs 34 | 35 | When working with the Ruff codebase, please adhere to the following guidelines: 36 | 37 | * **Understand the Core Concepts:** Before making changes, ensure you have a conceptual understanding of algebraic effects and handlers, and one-shot continuations. The `README.md` file provides a good starting point. 38 | * **Follow Existing Patterns:** The codebase has a clear structure. New effects, handlers, or other components should follow the existing patterns and conventions. 39 | * **Testing is Crucial:** Any new feature or bug fix must be accompanied by tests. The project uses RSpec for testing. Please add new tests to the `spec` directory and ensure all existing tests pass. 40 | * **Documentation:** For any user-facing changes, update the documentation. The project uses YARD for documentation. 41 | * **Dependencies:** Do not add new gem dependencies without a strong justification. This is a lightweight library and should remain so. 42 | * **Code Style:** The project follows standard Ruby style guidelines. 43 | * **Commit Messages:** Write clear and concise commit messages that explain the "why" behind the changes. 44 | -------------------------------------------------------------------------------- /docs/Ruff/Throws.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Ruff::Throws 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Ruff::Throws 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/ruff/objects.rb
82 |
83 | 84 |
85 | 86 |

Defined Under Namespace

87 |

88 | 89 | 90 | 91 | 92 | Classes: Eff, Resend 93 | 94 | 95 |

96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | 107 | 112 | 113 |
114 | 115 | -------------------------------------------------------------------------------- /docs/Util.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Util 8 | 9 | — Ruff 1.2.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Util 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/ruff/standard/util.rb
82 |
83 | 84 |
85 | 86 |

Defined Under Namespace

87 |

88 | 89 | 90 | Modules: ADT 91 | 92 | 93 | 94 | Classes: FnStack, Ref 95 | 96 | 97 |

98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
108 | 109 | 114 | 115 |
116 | 117 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "bundix": { 4 | "inputs": { 5 | "nixpkgs": [ 6 | "nixpkgs" 7 | ] 8 | }, 9 | "locked": { 10 | "lastModified": 1721093334, 11 | "narHash": "sha256-5FghZ0HIETbc0TGcBV8uyixq5z4w/9PF5Puhujz3D1o=", 12 | "owner": "inscapist", 13 | "repo": "bundix", 14 | "rev": "42c08846f7e5d91ef121239fd364feaeb22c0bbc", 15 | "type": "github" 16 | }, 17 | "original": { 18 | "owner": "inscapist", 19 | "ref": "main", 20 | "repo": "bundix", 21 | "type": "github" 22 | } 23 | }, 24 | "flake-utils": { 25 | "inputs": { 26 | "systems": "systems" 27 | }, 28 | "locked": { 29 | "lastModified": 1731533236, 30 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 31 | "owner": "numtide", 32 | "repo": "flake-utils", 33 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 34 | "type": "github" 35 | }, 36 | "original": { 37 | "owner": "numtide", 38 | "repo": "flake-utils", 39 | "type": "github" 40 | } 41 | }, 42 | "nixpkgs": { 43 | "locked": { 44 | "lastModified": 1756617294, 45 | "narHash": "sha256-aGnd4AHIYCWQKChAkHPpX+YYCt7pA6y2LFFA/s8q0wQ=", 46 | "rev": "b4c2c57c31e68544982226d07e4719a2d86302a8", 47 | "revCount": 809186, 48 | "type": "tarball", 49 | "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2505.809186%2Brev-b4c2c57c31e68544982226d07e4719a2d86302a8/019901ed-e9b5-723d-afee-cfeb962dad6c/source.tar.gz" 50 | }, 51 | "original": { 52 | "type": "tarball", 53 | "url": "https://flakehub.com/f/NixOS/nixpkgs/%2A" 54 | } 55 | }, 56 | "root": { 57 | "inputs": { 58 | "bundix": "bundix", 59 | "flake-utils": "flake-utils", 60 | "nixpkgs": "nixpkgs", 61 | "ruby-nix": "ruby-nix" 62 | } 63 | }, 64 | "ruby-nix": { 65 | "inputs": { 66 | "nixpkgs": [ 67 | "nixpkgs" 68 | ] 69 | }, 70 | "locked": { 71 | "lastModified": 1755059052, 72 | "narHash": "sha256-yUJmmNIw11ZEIAFogqcqNomk4YV3F/zjwI1f7bYzIyY=", 73 | "owner": "inscapist", 74 | "repo": "ruby-nix", 75 | "rev": "86b498e80058a84461d1f533e121574d85d272d7", 76 | "type": "github" 77 | }, 78 | "original": { 79 | "owner": "inscapist", 80 | "repo": "ruby-nix", 81 | "type": "github" 82 | } 83 | }, 84 | "systems": { 85 | "locked": { 86 | "lastModified": 1681028828, 87 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 88 | "owner": "nix-systems", 89 | "repo": "default", 90 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 91 | "type": "github" 92 | }, 93 | "original": { 94 | "owner": "nix-systems", 95 | "repo": "default", 96 | "type": "github" 97 | } 98 | } 99 | }, 100 | "root": "root", 101 | "version": 7 102 | } 103 | -------------------------------------------------------------------------------- /spec/ruff/effect_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe Ruff::Effect do 6 | let(:log_effect) { described_class.new } 7 | 8 | describe '#initialize' do 9 | it 'initializes with a unique id' do 10 | expect(log_effect.id).to be_a(Object) 11 | expect(log_effect.id).not_to eq(described_class.new.id) 12 | end 13 | end 14 | 15 | describe 'subtyping' do 16 | let(:exception_effect) { described_class.new } 17 | let(:runtime_exception_effect) { described_class << exception_effect } 18 | 19 | it 'creates an effect with a subtyping relation' do 20 | expect(runtime_exception_effect).to be_an_instance_of(described_class) 21 | expect(runtime_exception_effect.id).to be_a(Object) 22 | expect(runtime_exception_effect.id.class.superclass).to eq(exception_effect.id.class) 23 | end 24 | 25 | describe 'handler behavior' do 26 | let(:exception_effect) { described_class.new } 27 | let(:runtime_exception_effect) { described_class << exception_effect } 28 | 29 | it 'a handler for the parent effect catches the child effect' do 30 | caught_message = nil 31 | handler = Ruff.handler 32 | .on(exception_effect) do |k, msg| 33 | caught_message = msg 34 | k[] 35 | end 36 | 37 | handler.run do 38 | runtime_exception_effect.perform('Runtime Error!') 39 | end 40 | 41 | expect(caught_message).to eq('Runtime Error!') 42 | end 43 | end 44 | 45 | describe 'multilevel handler behavior' do 46 | it 'handles multilevel subtyping correctly' do 47 | grandparent_effect = described_class.new 48 | parent_effect = described_class << grandparent_effect 49 | child_effect = described_class << parent_effect 50 | 51 | handled_by = nil 52 | handler = 53 | Ruff.handler 54 | .on(grandparent_effect) do |k, msg| 55 | handled_by = "Grandparent: #{msg}" 56 | k[] 57 | end.on(parent_effect) do |k, msg| 58 | handled_by = "Parent: #{msg}" 59 | k[] 60 | end.on(child_effect) do |k, msg| 61 | handled_by = "Child: #{msg}" 62 | k[] 63 | end.to do |_| 64 | handled_by 65 | end 66 | 67 | expect(handler.run do 68 | child_effect.perform('from child') 69 | end).to eq('Child: from child') 70 | expect(handler.run do 71 | parent_effect.perform('from parent') 72 | end).to eq('Parent: from parent') 73 | expect(handler.run do 74 | grandparent_effect.perform('from grandparent') 75 | end).to eq('Grandparent: from grandparent') 76 | end 77 | end 78 | end 79 | 80 | describe '#perform' do 81 | let(:log_effect) { described_class.new } 82 | 83 | it 'yields a Ruff::Throws::Eff object' do 84 | fiber = Fiber.new do 85 | log_effect.perform('hello') 86 | end 87 | eff_obj = fiber.resume 88 | 89 | expect(eff_obj).to be_an_instance_of(Ruff::Throws::Eff) 90 | expect(eff_obj.id).to eq(log_effect.id) 91 | expect(eff_obj.args).to eq(['hello']) 92 | end 93 | 94 | it 'raises FiberError if performed outside a handler' do 95 | expect { log_effect.perform('no handler') }.to raise_error(FiberError) 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /lib/ruff/standard/state.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # `State` provides effects `State.get` and `State.modify` , 4 | # and the implementation of the mutable cell or so-called *state* . 5 | # 6 | # The module has an instance of `Instance` and provides its methods as module method. 7 | # @see Standard::State::Instance 8 | # @example 9 | # r = State.with { 10 | # State.put 10 11 | # puts State.get #==> 10 12 | # State.modify {|s| s + 20} 13 | # State.modify {|s| 14 | # puts s #==> 30 15 | # 0 16 | # } 17 | # 18 | # puts State.get #==> 0 19 | # 20 | # State.put 11 21 | # } 22 | # 23 | # puts r #==> 11 24 | module Ruff::Standard::State 25 | class Instance 26 | # makes new instances. 27 | def initialize 28 | # delegates effect instances. 29 | @eff = Struct.new(:get, :modify).new( 30 | Ruff.instance, 31 | Ruff.instance 32 | ) 33 | end 34 | 35 | # is a smart method to invoke the effect operation `State.get` . 36 | # @return [S] 37 | # with `with` , returns `S` , the current state. 38 | def get 39 | @eff.get.perform 40 | end 41 | 42 | # is a smart hetmod to invoke the effect operation `State.modify` . 43 | # @param [Proc] fn 44 | # is the function to modify the state `S` to `U` . 45 | # This function has an argument receiving the state. 46 | # @return [()] 47 | def modify(&fn) 48 | @eff.modify.perform fn 49 | end 50 | 51 | # is a short hand for `modify {|_| s }` 52 | # @param [S] s 53 | # is the new state. 54 | # @return [()] 55 | def put(s) 56 | @eff.modify.perform ->(_) { s } 57 | end 58 | 59 | # is a handler to interpret the effect invocations like *state monad* . 60 | # 61 | # @param [S] init 62 | # is the initial state. 63 | # @param [Proc<(), A!{State.get, State.modify,, e}>] th 64 | # is a thunk returning `A` with the possibility to invoke effects, 65 | # including `State.get` and `State.modify` . 66 | # @return [A!{e}] 67 | # returns `A` , without modification by value handler. 68 | # But it still has the possibility to invoke effects(`e`). 69 | def with_init(init, &th) 70 | # not a parameter passing style, or so-called *pure* implementation, 71 | # just using mutable assignment 72 | state = init 73 | 74 | # The handler *closes* `state` variable so it should be created every time. 75 | Ruff.handler 76 | .on(@eff.modify) do |k, fn| 77 | state = fn[state] 78 | k[nil] 79 | end.on(@eff.get) do |k| 80 | k[state] 81 | end 82 | .run(&th) 83 | end 84 | 85 | # is a short hand for `with_init(nil, th)` . 86 | # 87 | # @param [Proc<(), A!{State.get, State.modify,, e}>] th 88 | # is a thunk returning `A` with the possibility to invoke effects, 89 | # including `State.get` and `State.modify` . 90 | # @return [A!{e}] 91 | # returns `A` , without modification by value handler. 92 | # But it still has the possibility to invoke effects(`e`). 93 | def with(&th) 94 | with_init(nil, &th) 95 | end 96 | 97 | # You can reimplement the handler using these effect instances 98 | # with accessing `#eff.get` and `#eff.modify` . 99 | attr_reader :eff 100 | end 101 | 102 | # --- 103 | @inst = Instance.new 104 | @eff = @inst.eff 105 | 106 | # @see Instance#get 107 | def get 108 | @inst.get 109 | end 110 | 111 | # @see Instance#modify 112 | def modify(&fn) 113 | @inst.modify(&fn) 114 | end 115 | 116 | # @see Instance#put 117 | def put(s) 118 | @inst.put s 119 | end 120 | 121 | # @see Instance#with_init 122 | def with_init(init, &task) 123 | @inst.with_init init, &task 124 | end 125 | 126 | # @see Instance#with 127 | def with(&task) 128 | @inst.with(&task) 129 | end 130 | 131 | module_function :get, :put, :modify, :with, :with_init 132 | 133 | # @see Instance#eff 134 | attr_reader :eff 135 | end 136 | -------------------------------------------------------------------------------- /spec/ruff/standard_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | RSpec.describe Ruff::Standard do 5 | describe Ruff::Standard::Async do 6 | it 'handles async/await operations' do 7 | results = [] 8 | described_class.with do 9 | task_a = described_class.async(proc do 10 | sleep(0.1) # Simulate some work 11 | results << 'Task A Done' 12 | 10 13 | end) 14 | task_b = described_class.async(proc do 15 | results << 'Task B Started' 16 | described_class.yield 17 | results << 'Task B Resumed' 18 | 20 19 | end) 20 | 21 | sum = described_class.await(task_a) + described_class.await(task_b) 22 | results << "Sum: #{sum}" 23 | end 24 | expect(results).to include('Task B Started', 'Task A Done', 'Task B Resumed', 'Sum: 30') 25 | expect(results.last).to eq('Sum: 30') 26 | end 27 | end 28 | 29 | describe Ruff::Standard::CurrentTime do 30 | it 'gets the current time' do 31 | time_before = Time.now 32 | result_time = nil 33 | described_class.with do 34 | result_time = described_class.get 35 | end 36 | time_after = Time.now 37 | expect(result_time).to be_between(time_before, time_after) 38 | end 39 | end 40 | 41 | describe Ruff::Standard::Defer do 42 | it 'executes registered procs at the end of the computation in reverse order' do 43 | execution_order = [] 44 | described_class.with do 45 | execution_order << 'start' 46 | described_class.register { execution_order << 'defer 1' } 47 | execution_order << 'middle' 48 | described_class.register { execution_order << 'defer 2' } 49 | execution_order << 'end' 50 | end 51 | expect(execution_order).to eq(['start', 'middle', 'end', 'defer 2', 'defer 1']) 52 | end 53 | end 54 | 55 | describe Ruff::Standard::DelimCtrl do 56 | it 'handles delimited continuations with shift and reset' do 57 | output = [] 58 | described_class.reset do 59 | output << 'hello' 60 | described_class.shift do |k| 61 | k.call 62 | output << '!' 63 | end 64 | output << 'world' 65 | end 66 | expect(output).to eq(['hello', 'world', '!']) 67 | end 68 | 69 | it 'captures and resumes continuation multiple times' do 70 | output = [] 71 | described_class.reset do 72 | x = described_class.shift do |k| 73 | output << 'shifted' 74 | k.call(1) # Resume with 1 75 | output << 'shift_end' 76 | 3 # Return value from shift if not resumed 77 | end 78 | output << "x is #{x}" 79 | end 80 | expect(output).to eq(['shifted', 'x is 1', 'shift_end']) 81 | end 82 | end 83 | 84 | describe Ruff::Standard::MeasureTime do 85 | it 'measures execution time of blocks' do 86 | results = nil 87 | described_class.with do 88 | described_class.measure 'first_task' 89 | sleep(0.01) 90 | described_class.measure 'second_task' 91 | sleep(0.02) 92 | 42 93 | end.tap do |res| 94 | results = res 95 | end 96 | 97 | expect(results.first).to eq(42) 98 | expect(results.last[:label]).to eq('first_task') 99 | expect(results.last[:time]).to be_within(0.05).of(0.03) 100 | expect(results[1][:time]).to be_within(0.05).of(0.02) 101 | expect(results[1][:label]).to eq('second_task') 102 | expect(results[1][:time]).to be_within(0.05).of(0.02) 103 | # Time from first measure to second measure 104 | end 105 | end 106 | 107 | describe Ruff::Standard::State do 108 | it 'manages state with get, put, and modify' do 109 | final_state = nil 110 | described_class.with_init(0) do 111 | described_class.put(10) 112 | expect(described_class.get).to eq(10) 113 | described_class.modify { |s| s + 5 } 114 | expect(described_class.get).to eq(15) 115 | described_class.put(20) 116 | final_state = described_class.get 117 | end 118 | expect(final_state).to eq(20) 119 | end 120 | 121 | it 'uses nil as initial state with #with' do 122 | retrieved_state = nil 123 | described_class.with do 124 | retrieved_state = described_class.get 125 | described_class.put('hello') 126 | end 127 | expect(retrieved_state).to be_nil 128 | end 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /docs/Util/ADT.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Util::ADT 8 | 9 | — Ruff 1.2.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Util::ADT 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/ruff/standard/util.rb
82 |
83 | 84 |
85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 |

95 | Class Method Summary 96 | collapse 97 |

98 | 99 |
    100 | 101 |
  • 102 | 103 | 104 | .create ⇒ Object 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
    119 | 120 |
  • 121 | 122 | 123 |
124 | 125 | 126 | 127 | 128 |
129 |

Class Method Details

130 | 131 | 132 |
133 |

134 | 135 | .createObject 136 | 137 | 138 | 139 | 140 | 141 |

142 | 143 | 159 | 174 | 175 |
144 |
145 | 
146 | 
147 | 40
148 | 41
149 | 42
150 | 43
151 | 44
152 | 45
153 | 46
154 | 47
155 | 48
156 | 49
157 | 50
158 |
160 |
# File 'lib/ruff/standard/util.rb', line 40
161 | 
162 | def create
163 |   Class.new do
164 |     def initialize(v)
165 |       @value = v
166 |     end
167 | 
168 |     def get
169 |       @value
170 |     end
171 |   end
172 | end
173 |
176 |
177 | 178 |
179 | 180 |
181 | 182 | 187 | 188 |
189 | 190 | -------------------------------------------------------------------------------- /lib/ruff/handler.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # In algebraic effects, handler is an first-class object. 4 | class Ruff::Handler 5 | # makes a new handler, internally having fresh empty hash. 6 | # 7 | # This is a effect-handler store and when handliong it is looked up. 8 | # Value handler is set an identity function to by default. 9 | 10 | class Store 11 | # is a private class to manage registered handlers 12 | def initialize 13 | @handler = {} 14 | end 15 | 16 | def []=(r, e) 17 | @handler[r.id] = e 18 | end 19 | 20 | def [](eff) 21 | # get a set {(eff', f) | forall (eff', f) in store. eff <: eff'} 22 | fns = @handler.filter do |effky, _fun| 23 | eff.id.is_a? effky.class 24 | end 25 | 26 | # pick a function with *smallest* effect class 27 | return fns.min_by { |effky, _| effky.class }[1] unless fns.empty? 28 | 29 | # if found nothing 30 | nil 31 | end 32 | end 33 | 34 | private_constant :Store 35 | 36 | # @example 37 | # handler = Handler.new 38 | def initialize 39 | @handlers = Store.new 40 | @valh = ->(x) { x } 41 | end 42 | 43 | # sets value handler `&fun`. 44 | # 45 | # Value handler is the handler for *the result value of the computation*. 46 | # For example, `Handler.new.to{|_x| 0}.run { value }` results in `0` . 47 | # 48 | # The value handler modifies the result of the call of continuation 49 | # in effect handlers of the handler. 50 | # 51 | # @param [Proc] fun 52 | # value handler 53 | # @return [Handler] 54 | # 55 | # @example 56 | # logs = [] 57 | # handler.on(Log) {|k, msg| 58 | # logs << msg 59 | # k[] 60 | # }.to {|x| 61 | # logs.each {|log| 62 | # puts "Logger: #{log}" 63 | # } 64 | # 65 | # puts "returns #{x}" 66 | # } 67 | # .run { 68 | # Log.perform "hello" 69 | # Log.perform "world" 70 | # "!" 71 | # } 72 | # 73 | # ## ==> 74 | # # msg>> hello 75 | # # msg>> world 76 | # # returns ! 77 | def to(&fun) 78 | @valh = fun 79 | 80 | self 81 | end 82 | 83 | # sets or updates effec handler `&fun` for `eff` 84 | # 85 | # Note that `eff` can be a supertype of an effect to be caught. 86 | # @see Effect.<< 87 | # 88 | # @param [Effect] eff 89 | # the effect instance to be handled 90 | # @param [Proc A>] fun 91 | # a handler to handle `eff`; 92 | # First argument of `&fun` is *continuation*, proc object 93 | # to go back to the handled computation. 94 | # @return [Handler, e}, B!{e}>] 95 | # itself updated with handling `Effect` 96 | # 97 | # @example 98 | # handler.on(Log) {|k, msg| 99 | # puts "Logger: #{msg}" 100 | # k[] 101 | # } 102 | def on(eff, &fun) 103 | @handlers[eff] = fun 104 | 105 | self 106 | end 107 | 108 | # handles the computation. 109 | # 110 | # @param [Proc<(), A>] prc 111 | # a thunk to be handled and returns `A` 112 | # @return [B] 113 | # a value modified by value handler `Proc` , 114 | # or returned from the effect handler throwing continuation away 115 | # 116 | # @example 117 | # handler.run { 118 | # Log.perform "hello" 119 | # } 120 | # 121 | # @example `handler` can be layered. 122 | # handler.run { 123 | # Handler.new 124 | # .on(Double){|k, v| 125 | # k[v * v] 126 | # }.run { 127 | # v = Double.perform 3 128 | # Log.perform 3 129 | # } 130 | # } 131 | def run(&prc) 132 | co = Fiber.new(&prc) 133 | 134 | continue(co).call(nil) 135 | end 136 | 137 | protected 138 | 139 | # receives `handlers` as new handlers. 140 | def handlers=(handlers) 141 | @handlers = handlers.dup 142 | end 143 | 144 | def continue(co) 145 | ->(*arg) { handle(co, co.resume(*arg)) } 146 | end 147 | 148 | # rubocop:disable Metrics/AbcSize 149 | def handle(co, r) 150 | case r 151 | when Ruff::Throws::Eff 152 | if (effh = @handlers[r]) 153 | effh[continue(co), *r.args] 154 | else 155 | Fiber.yield Ruff::Throws::Resend.new(r, continue(co)) 156 | end 157 | when Ruff::Throws::Resend 158 | eff = r.eff 159 | next_k = rehandles(co, r.k) 160 | 161 | if (effh = @handlers[eff]) 162 | effh.call(next_k, *eff.args) 163 | else 164 | Fiber.yield Ruff::Throws::Resend.new(eff, next_k) 165 | end 166 | else 167 | @valh.call(r) 168 | end 169 | end 170 | # rubocop:enable Metrics/AbcSize 171 | 172 | def rehandles(co, k) 173 | newh = self.class.new 174 | 175 | newh.handlers = @handlers 176 | 177 | lambda { |*args| 178 | newh 179 | .to { |v| continue(co).call(v) } 180 | .run { k.call(*args) } 181 | } 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /lib/ruff/standard/async.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # `Async` provides effects `Async.async`, `Async.yield` and `Async.await`, 4 | # and the implementation async/await. 5 | # This implementation is based on the tutorial for Multicore OCaml. 6 | # @see https://github.com/ocamllabs/ocaml-effects-tutorial/blob/master/sources/solved/async_await.ml 7 | # 8 | # The module has an instance of `Instance` and provides its methods as module method. 9 | # @see Standard::Async::Instance 10 | # @example 11 | # Async.with do 12 | # task = lambda { |name| 13 | # lambda { 14 | # puts "Starting #{name}" 15 | # v = (Random.rand * (10**3)).floor 16 | # puts "Yielding #{name}" 17 | # Async.yield 18 | # puts "Eidnig #{name} with #{v}" 19 | # 20 | # v 21 | # } 22 | # } 23 | # 24 | # pa = Async.async(task.call('a')) 25 | # pb = Async.async(task.call('b')) 26 | # pc = Async.async lambda { 27 | # Async.await(pa) + Async.await(pb) 28 | # } 29 | # 30 | # puts "sum is #{Async.await pc}" 31 | # end 32 | # #==> 33 | # # Starting a 34 | # # Yielding a 35 | # # Eidnig a with 423 36 | # # Starting b 37 | # # Yielding b 38 | # # Eidnig b with 793 39 | # # sum is 1216 40 | module Ruff::Standard::Async 41 | class Instance 42 | require 'ostruct' 43 | require 'ruff/standard/util' 44 | 45 | # are ADT-like classes which have only getter method. 46 | # These are used internally. 47 | # 48 | # type 'a promise = 49 | # | Waiting of ('a, unit) continuation list 50 | # | Done of 'a 51 | Waiting = Util::ADT.create 52 | Done = Util::ADT.create 53 | 54 | # makes a new instance. 55 | def initialize 56 | # delegates effect instances. 57 | @eff = Struct.new(:async, :yield, :await).new( 58 | Ruff.instance, 59 | Ruff.instance, 60 | Ruff.instance 61 | ) 62 | 63 | # is a proc queue. 64 | @q = Util::FnStack.new 65 | end 66 | 67 | # is a smart method to invoke the effect operation `Async.async` . 68 | # @param [Proc<(), A>] th 69 | # is a thunk asynchronously computed. 70 | # @return [Promise] 71 | # with `with` , returns `Promise` with running concurrently . 72 | def async(th) 73 | @eff.async.perform th 74 | end 75 | 76 | # is a smart method to invoke the effect operation `Async.yield` . 77 | # @return [()] 78 | # with `with` , yields the control to another task. 79 | def yield 80 | @eff.yield.perform 81 | end 82 | 83 | # is a smart method to invoke the effect operation `Async.await` . 84 | # @param [Promise] p 85 | # is a promise to run. 86 | # @return [A] 87 | # with `with` returns the result of *promise* computation. 88 | def await(p) 89 | @eff.await.perform p 90 | end 91 | 92 | # @method with(&th) 93 | # @param [Proc<(), _A!{Async.async, Async.yield, Async.await, e}>] th 94 | # is a thunk returning `_A` with te possibility to invoke effects, 95 | # including `Async.async` , `Async.yield` and `Async.await` . 96 | # @return [()!{e}] 97 | # returns unit but still has the possibility to invoke effects `e` . 98 | define_method :with do |&th| 99 | fork(Util::Ref.new(Waiting.new([])), th) 100 | end 101 | 102 | # You can reimplement the handler using these effect instances 103 | # with accessing `Async.async` , `Async.yield` , and `Async.await` . 104 | attr_reader :eff 105 | 106 | private 107 | 108 | # rubocop:disable Metrics/AbcSize 109 | # rubocop:disable Metrics/BlockLength 110 | define_method :fork do |pr, th| 111 | h = Ruff::Handler.new 112 | h.to do |v| 113 | pp = pr.get 114 | l = case pp 115 | when Waiting then pp.get 116 | else raise 'impossible' 117 | end 118 | 119 | l.each do |k| 120 | @q.enqueue(-> { k[v] }) 121 | end 122 | 123 | pr.set(Done.new(v)) 124 | @q.dequeue 125 | end 126 | 127 | h.on(@eff.async) do |k, f| 128 | pr_ = Util::Ref.new(Waiting.new([])) 129 | @q.enqueue(-> { k[pr_] }) 130 | fork(pr_, f) 131 | end 132 | h.on(@eff.yield) do |k| 133 | @q.enqueue(-> { k[] }) 134 | @q.dequeue.call 135 | end 136 | 137 | h.on(@eff.await) do |k, ppr| 138 | pp = ppr.get 139 | 140 | return case pp 141 | when Done then k[pp.get] 142 | when Waiting 143 | pr.set(Waiting.new(@q.cons(k))) 144 | @q.dequeue.call 145 | end 146 | end 147 | 148 | h.run { th[] } 149 | end 150 | # rubocop:enable Metrics/BlockLength 151 | # rubocop:enable Metrics/AbcSize 152 | end 153 | 154 | # --- 155 | @inst = Instance.new 156 | @eff = @inst.eff 157 | 158 | # @see Instance#async 159 | def async(asynth) 160 | @inst.async asynth 161 | end 162 | 163 | # @see Instance#await 164 | def await(p) 165 | @inst.await p 166 | end 167 | 168 | # @see Instance#yield 169 | def yield 170 | @inst.yield 171 | end 172 | 173 | # @see Instance#with 174 | def with(&th) 175 | @inst.with(&th) 176 | end 177 | 178 | module_function :async, :await, :yield, :with 179 | 180 | # @see Instance#eff 181 | attr_reader :eff 182 | end 183 | -------------------------------------------------------------------------------- /docs/css/full_list.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif; 4 | font-size: 13px; 5 | height: 101%; 6 | overflow-x: hidden; 7 | background: #fafafa; 8 | } 9 | 10 | h1 { padding: 12px 10px; padding-bottom: 0; margin: 0; font-size: 1.4em; } 11 | .clear { clear: both; } 12 | .fixed_header { position: fixed; background: #fff; width: 100%; padding-bottom: 10px; margin-top: 0; top: 0; z-index: 9999; height: 70px; } 13 | #search { position: absolute; right: 5px; top: 9px; padding-left: 24px; } 14 | #content.insearch #search, #content.insearch #noresults { background: url(data:image/gif;base64,R0lGODlhEAAQAPYAAP///wAAAPr6+pKSkoiIiO7u7sjIyNjY2J6engAAAI6OjsbGxjIyMlJSUuzs7KamppSUlPLy8oKCghwcHLKysqSkpJqamvT09Pj4+KioqM7OzkRERAwMDGBgYN7e3ujo6Ly8vCoqKjY2NkZGRtTU1MTExDw8PE5OTj4+PkhISNDQ0MrKylpaWrS0tOrq6nBwcKysrLi4uLq6ul5eXlxcXGJiYoaGhuDg4H5+fvz8/KKiohgYGCwsLFZWVgQEBFBQUMzMzDg4OFhYWBoaGvDw8NbW1pycnOLi4ubm5kBAQKqqqiQkJCAgIK6urnJyckpKSjQ0NGpqatLS0sDAwCYmJnx8fEJCQlRUVAoKCggICLCwsOTk5ExMTPb29ra2tmZmZmhoaNzc3KCgoBISEiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCAAAACwAAAAAEAAQAAAHaIAAgoMgIiYlg4kACxIaACEJCSiKggYMCRselwkpghGJBJEcFgsjJyoAGBmfggcNEx0flBiKDhQFlIoCCA+5lAORFb4AJIihCRbDxQAFChAXw9HSqb60iREZ1omqrIPdJCTe0SWI09GBACH5BAkIAAAALAAAAAAQABAAAAdrgACCgwc0NTeDiYozCQkvOTo9GTmDKy8aFy+NOBA7CTswgywJDTIuEjYFIY0JNYMtKTEFiRU8Pjwygy4ws4owPyCKwsMAJSTEgiQlgsbIAMrO0dKDGMTViREZ14kYGRGK38nHguHEJcvTyIEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDAggPg4iJAAMJCRUAJRIqiRGCBI0WQEEJJkWDERkYAAUKEBc4Po1GiKKJHkJDNEeKig4URLS0ICImJZAkuQAhjSi/wQyNKcGDCyMnk8u5rYrTgqDVghgZlYjcACTA1sslvtHRgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCQARAtOUoQRGRiFD0kJUYWZhUhKT1OLhR8wBaaFBzQ1NwAlkIszCQkvsbOHL7Y4q4IuEjaqq0ZQD5+GEEsJTDCMmIUhtgk1lo6QFUwJVDKLiYJNUd6/hoEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4uen4ICCA+IkIsDCQkVACWmhwSpFqAABQoQF6ALTkWFnYMrVlhWvIKTlSAiJiVVPqlGhJkhqShHV1lCW4cMqSkAR1ofiwsjJyqGgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCSMhREZGIYYGY2ElYebi56fhyWQniSKAKKfpaCLFlAPhl0gXYNGEwkhGYREUywag1wJwSkHNDU3D0kJYIMZQwk8MjPBLx9eXwuETVEyAC/BOKsuEjYFhoEAIfkECQgAAAAsAAAAABAAEAAAB2eAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4ueICImip6CIQkJKJ4kigynKaqKCyMnKqSEK05StgAGQRxPYZaENqccFgIID4KXmQBhXFkzDgOnFYLNgltaSAAEpxa7BQoQF4aBACH5BAkIAAAALAAAAAAQABAAAAdogACCg4SFggJiPUqCJSWGgkZjCUwZACQkgxGEXAmdT4UYGZqCGWQ+IjKGGIUwPzGPhAc0NTewhDOdL7Ykji+dOLuOLhI2BbaFETICx4MlQitdqoUsCQ2vhKGjglNfU0SWmILaj43M5oEAOwAAAAAAAAAAAA==) no-repeat center left; } 15 | #full_list { padding: 0; list-style: none; margin-left: 0; margin-top: 80px; font-size: 1.1em; } 16 | #full_list ul { padding: 0; } 17 | #full_list li { padding: 0; margin: 0; list-style: none; } 18 | #full_list li .item { padding: 5px 5px 5px 12px; } 19 | #noresults { padding: 7px 12px; background: #fff; } 20 | #content.insearch #noresults { margin-left: 7px; } 21 | li.collapsed ul { display: none; } 22 | li a.toggle { cursor: default; position: relative; left: -5px; top: 4px; text-indent: -999px; width: 10px; height: 9px; margin-left: -10px; display: block; float: left; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTM5jWRgMAAAAVdEVYdENyZWF0aW9uIFRpbWUAMy8xNC8wOeNZPpQAAAE2SURBVDiNrZTBccIwEEXfelIAHUA6CZ24BGaWO+FuzZAK4k6gg5QAdGAq+Bxs2Yqx7BzyL7Llp/VfzZeQhCTc/ezuGzKKnKSzpCxXJM8fwNXda3df5RZETlIt6YUzSQDs93sl8w3wBZxCCE10GM1OcWbWjB2mWgEH4Mfdyxm3PSepBHibgQE2wLe7r4HjEidpnXMYdQPKEMJcsZ4zs2POYQOcaPfwMVOo58zsAdMt18BuoVDPxUJRacELbXv3hUIX2vYmOUvi8C8ydz/ThjXrqKqqLbDIAdsCKBd+Wo7GWa7o9qzOQHVVVXeAbs+yHHCH4aTsaCOQqunmUy1yBUAXkdMIfMlgF5EXLo2OpV/c/Up7jG4hhHcYLgWzAZXUc2b2ixsfvc/RmNNfOXD3Q/oeL9axJE1yT9IOoUu6MGUkAAAAAElFTkSuQmCC) no-repeat bottom left; } 23 | li.collapsed a.toggle { opacity: 0.5; cursor: default; background-position: top left; } 24 | li { color: #888; cursor: pointer; } 25 | li.deprecated { text-decoration: line-through; font-style: italic; } 26 | li.odd { background: #f0f0f0; } 27 | li.even { background: #fafafa; } 28 | .item:hover { background: #ddd; } 29 | li small:before { content: "("; } 30 | li small:after { content: ")"; } 31 | li small.search_info { display: none; } 32 | a, a:visited { text-decoration: none; color: #05a; } 33 | li.clicked > .item { background: #05a; color: #ccc; } 34 | li.clicked > .item a, li.clicked > .item a:visited { color: #eee; } 35 | li.clicked > .item a.toggle { opacity: 0.5; background-position: bottom right; } 36 | li.collapsed.clicked a.toggle { background-position: top right; } 37 | #search input { border: 1px solid #bbb; border-radius: 3px; } 38 | #full_list_nav { margin-left: 10px; font-size: 0.9em; display: block; color: #aaa; } 39 | #full_list_nav a, #nav a:visited { color: #358; } 40 | #full_list_nav a:hover { background: transparent; color: #5af; } 41 | #full_list_nav span:after { content: ' | '; } 42 | #full_list_nav span:last-child:after { content: ''; } 43 | 44 | #content h1 { margin-top: 0; } 45 | li { white-space: nowrap; cursor: normal; } 46 | li small { display: block; font-size: 0.8em; } 47 | li small:before { content: ""; } 48 | li small:after { content: ""; } 49 | li small.search_info { display: none; } 50 | #search { width: 170px; position: static; margin: 3px; margin-left: 10px; font-size: 0.9em; color: #888; padding-left: 0; padding-right: 24px; } 51 | #content.insearch #search { background-position: center right; } 52 | #search input { width: 110px; } 53 | 54 | #full_list.insearch ul { display: block; } 55 | #full_list.insearch .item { display: none; } 56 | #full_list.insearch .found { display: block; padding-left: 11px !important; } 57 | #full_list.insearch li a.toggle { display: none; } 58 | #full_list.insearch li small.search_info { display: block; } 59 | -------------------------------------------------------------------------------- /spec/ruff/handler_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | RSpec.describe Ruff::Handler do 6 | describe '#to' do 7 | let(:log_effect) { Ruff::Effect.new } 8 | let(:handler) { Ruff.handler } 9 | 10 | it 'modifies the result of the computation' do 11 | result = handler.to { |x| x * 2 }.run { 5 } 12 | expect(result).to eq(10) 13 | end 14 | 15 | it 'interacts with effect handlers' do 16 | messages = [] 17 | result = handler.on(log_effect) do |k, msg| 18 | messages << msg 19 | k[] 20 | end.to do |x| 21 | messages << "Final result: #{x}" 22 | messages.join(', ') 23 | end.run do 24 | log_effect.perform('msg1') 25 | log_effect.perform('msg2') 26 | 100 27 | end 28 | expect(result).to eq('msg1, msg2, Final result: 100') 29 | end 30 | end 31 | 32 | describe '#on' do 33 | describe 'effect handler registration' do 34 | let(:log_effect) { Ruff::Effect.new } 35 | let(:handler) { Ruff.handler } 36 | 37 | it 'registers a handler for a given effect' do 38 | caught_message = nil 39 | handler.on(log_effect) do |k, msg| 40 | caught_message = msg 41 | k[] 42 | end.run do 43 | log_effect.perform('Test Message') 44 | end 45 | expect(caught_message).to eq('Test Message') 46 | end 47 | end 48 | 49 | describe 'subtyping' do 50 | let(:handler) { Ruff.handler } 51 | 52 | it 'chooses the most specific handler when subtyping is involved' do 53 | parent_eff = Ruff::Effect.new 54 | child_eff = Ruff::Effect << parent_eff 55 | 56 | handled_by = nil 57 | handler = Ruff.handler 58 | .on(parent_eff) do |k, msg| 59 | handled_by = "Parent: #{msg}" 60 | k[] 61 | end.on(child_eff) do |k, msg| 62 | handled_by = "Child: #{msg}" 63 | k[] 64 | end 65 | 66 | handler.run { child_eff.perform('specific') } 67 | expect(handled_by).to eq('Child: specific') 68 | 69 | handler.run { parent_eff.perform('general') } 70 | expect(handled_by).to eq('Parent: general') 71 | end 72 | end 73 | end 74 | 75 | describe '#run' do 76 | describe 'handling computation' do 77 | let(:log_effect) { Ruff::Effect.new } 78 | let(:handler) { Ruff.handler } 79 | 80 | it 'handles a basic effect' do 81 | caught_message = nil 82 | handler.on(log_effect) do |k, msg| 83 | caught_message = msg 84 | k[] 85 | end.run do 86 | log_effect.perform('Basic Test') 87 | end 88 | expect(caught_message).to eq('Basic Test') 89 | end 90 | 91 | it 'returns the final value from the computation' do 92 | result = handler.on(log_effect) { |k, _| k[] }.run { 'Final Value' } 93 | expect(result).to eq('Final Value') 94 | end 95 | end 96 | 97 | describe 'layered handlers' do 98 | let(:log_effect) { Ruff::Effect.new } 99 | let(:handler) { Ruff.handler } 100 | 101 | it 'handles layered handlers (nested run blocks)' do 102 | double_effect = Ruff::Effect.new 103 | log_messages = [] 104 | 105 | outer_handler = Ruff.handler.on(log_effect) do |k, msg| 106 | log_messages << "Outer Log: #{msg}" 107 | k[] 108 | end 109 | 110 | inner_handler = Ruff.handler.on(double_effect) do |k, val| 111 | log_messages << "Inner Double: #{val}" 112 | k[val * 2] 113 | end 114 | 115 | result = outer_handler.run do 116 | log_effect.perform('Starting outer') 117 | inner_handler.run do 118 | log_effect.perform('Starting inner') # This should be handled by outer_handler 119 | val = double_effect.perform(5) 120 | log_effect.perform("After double: #{val}") 121 | val + 1 122 | end 123 | end 124 | expect(result).to eq(11) # (5 * 2) + 1 125 | 126 | expect(log_messages).to eq([ 127 | 'Outer Log: Starting outer', 128 | 'Outer Log: Starting inner', 129 | 'Inner Double: 5', 130 | 'Outer Log: After double: 10' 131 | ]) 132 | end 133 | end 134 | 135 | describe 'unhandled effects' do 136 | let(:unhandled_effect) { Ruff::Effect.new } 137 | let(:handler) { Ruff.handler } 138 | 139 | it 'propagates unhandled effects as Resend' do 140 | caught_resend_eff = nil 141 | 142 | # Outer handler to catch the Resend 143 | outer_handler = Ruff.handler.on(unhandled_effect) do |k, msg| 144 | caught_resend_eff = msg 145 | k[] 146 | end 147 | 148 | # Inner computation that performs an unhandled effect 149 | outer_handler.run do 150 | unhandled_effect.perform('This should be resent') 151 | end 152 | 153 | expect(caught_resend_eff).to eq('This should be resent') 154 | end 155 | 156 | it 'allows an effect handler to perform another effect sequentially' do 157 | effect_a = Ruff::Effect.new 158 | effect_b = Ruff::Effect.new 159 | messages = [] 160 | 161 | handler_a = Ruff.handler.on(effect_a) do |k, msg_a| 162 | messages << "Handled A: #{msg_a}" 163 | k[] 164 | end 165 | 166 | handler_b = Ruff.handler.on(effect_b) do |k, msg_b| 167 | messages << "Handled B: #{msg_b}" 168 | k[] 169 | end 170 | 171 | handler_b.run do 172 | handler_a.run do 173 | effect_a.perform('initial') 174 | end 175 | effect_b.perform('second') 176 | messages << 'Computation finished' 177 | end 178 | 179 | expect(messages).to eq([ 180 | 'Handled A: initial', 181 | 'Handled B: second', 182 | 'Computation finished' 183 | ]) 184 | end 185 | end 186 | end 187 | end 188 | -------------------------------------------------------------------------------- /docs/js/full_list.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var $clicked = $(null); 4 | var searchTimeout = null; 5 | var searchCache = []; 6 | var caseSensitiveMatch = false; 7 | var ignoreKeyCodeMin = 8; 8 | var ignoreKeyCodeMax = 46; 9 | var commandKey = 91; 10 | 11 | RegExp.escape = function(text) { 12 | return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 13 | } 14 | 15 | function escapeShortcut() { 16 | $(document).keydown(function(evt) { 17 | if (evt.which == 27) { 18 | window.parent.postMessage('navEscape', '*'); 19 | } 20 | }); 21 | } 22 | 23 | function navResizer() { 24 | $(window).mousemove(function(e) { 25 | window.parent.postMessage({ 26 | action: 'mousemove', event: {pageX: e.pageX, which: e.which} 27 | }, '*'); 28 | }).mouseup(function(e) { 29 | window.parent.postMessage({action: 'mouseup'}, '*'); 30 | }); 31 | window.parent.postMessage("navReady", "*"); 32 | } 33 | 34 | function clearSearchTimeout() { 35 | clearTimeout(searchTimeout); 36 | searchTimeout = null; 37 | } 38 | 39 | function enableLinks() { 40 | // load the target page in the parent window 41 | $('#full_list li').on('click', function(evt) { 42 | $('#full_list li').removeClass('clicked'); 43 | $clicked = $(this); 44 | $clicked.addClass('clicked'); 45 | evt.stopPropagation(); 46 | 47 | if (evt.target.tagName === 'A') return true; 48 | 49 | var elem = $clicked.find('> .item .object_link a')[0]; 50 | var e = evt.originalEvent; 51 | var newEvent = new MouseEvent(evt.originalEvent.type); 52 | newEvent.initMouseEvent(e.type, e.canBubble, e.cancelable, e.view, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget); 53 | elem.dispatchEvent(newEvent); 54 | evt.preventDefault(); 55 | return false; 56 | }); 57 | } 58 | 59 | function enableToggles() { 60 | // show/hide nested classes on toggle click 61 | $('#full_list a.toggle').on('click', function(evt) { 62 | evt.stopPropagation(); 63 | evt.preventDefault(); 64 | $(this).parent().parent().toggleClass('collapsed'); 65 | highlight(); 66 | }); 67 | } 68 | 69 | function populateSearchCache() { 70 | $('#full_list li .item').each(function() { 71 | var $node = $(this); 72 | var $link = $node.find('.object_link a'); 73 | if ($link.length > 0) { 74 | searchCache.push({ 75 | node: $node, 76 | link: $link, 77 | name: $link.text(), 78 | fullName: $link.attr('title').split(' ')[0] 79 | }); 80 | } 81 | }); 82 | } 83 | 84 | function enableSearch() { 85 | $('#search input').keyup(function(event) { 86 | if (ignoredKeyPress(event)) return; 87 | if (this.value === "") { 88 | clearSearch(); 89 | } else { 90 | performSearch(this.value); 91 | } 92 | }); 93 | 94 | $('#full_list').after(""); 95 | } 96 | 97 | function ignoredKeyPress(event) { 98 | if ( 99 | (event.keyCode > ignoreKeyCodeMin && event.keyCode < ignoreKeyCodeMax) || 100 | (event.keyCode == commandKey) 101 | ) { 102 | return true; 103 | } else { 104 | return false; 105 | } 106 | } 107 | 108 | function clearSearch() { 109 | clearSearchTimeout(); 110 | $('#full_list .found').removeClass('found').each(function() { 111 | var $link = $(this).find('.object_link a'); 112 | $link.text($link.text()); 113 | }); 114 | $('#full_list, #content').removeClass('insearch'); 115 | $clicked.parents().removeClass('collapsed'); 116 | highlight(); 117 | } 118 | 119 | function performSearch(searchString) { 120 | clearSearchTimeout(); 121 | $('#full_list, #content').addClass('insearch'); 122 | $('#noresults').text('').hide(); 123 | partialSearch(searchString, 0); 124 | } 125 | 126 | function partialSearch(searchString, offset) { 127 | var lastRowClass = ''; 128 | var i = null; 129 | for (i = offset; i < Math.min(offset + 50, searchCache.length); i++) { 130 | var item = searchCache[i]; 131 | var searchName = (searchString.indexOf('::') != -1 ? item.fullName : item.name); 132 | var matchString = buildMatchString(searchString); 133 | var matchRegexp = new RegExp(matchString, caseSensitiveMatch ? "" : "i"); 134 | if (searchName.match(matchRegexp) == null) { 135 | item.node.removeClass('found'); 136 | item.link.text(item.link.text()); 137 | } 138 | else { 139 | item.node.addClass('found'); 140 | item.node.removeClass(lastRowClass).addClass(lastRowClass == 'r1' ? 'r2' : 'r1'); 141 | lastRowClass = item.node.hasClass('r1') ? 'r1' : 'r2'; 142 | item.link.html(item.name.replace(matchRegexp, "$&")); 143 | } 144 | } 145 | if(i == searchCache.length) { 146 | searchDone(); 147 | } else { 148 | searchTimeout = setTimeout(function() { 149 | partialSearch(searchString, i); 150 | }, 0); 151 | } 152 | } 153 | 154 | function searchDone() { 155 | searchTimeout = null; 156 | highlight(); 157 | if ($('#full_list li:visible').size() === 0) { 158 | $('#noresults').text('No results were found.').hide().fadeIn(); 159 | } else { 160 | $('#noresults').text('').hide(); 161 | } 162 | $('#content').removeClass('insearch'); 163 | } 164 | 165 | function buildMatchString(searchString, event) { 166 | caseSensitiveMatch = searchString.match(/[A-Z]/) != null; 167 | var regexSearchString = RegExp.escape(searchString); 168 | if (caseSensitiveMatch) { 169 | regexSearchString += "|" + 170 | $.map(searchString.split(''), function(e) { return RegExp.escape(e); }). 171 | join('.+?'); 172 | } 173 | return regexSearchString; 174 | } 175 | 176 | function highlight() { 177 | $('#full_list li:visible').each(function(n) { 178 | $(this).removeClass('even odd').addClass(n % 2 == 0 ? 'odd' : 'even'); 179 | }); 180 | } 181 | 182 | /** 183 | * Expands the tree to the target element and its immediate 184 | * children. 185 | */ 186 | function expandTo(path) { 187 | var $target = $(document.getElementById('object_' + path)); 188 | $target.addClass('clicked'); 189 | $target.removeClass('collapsed'); 190 | $target.parentsUntil('#full_list', 'li').removeClass('collapsed'); 191 | if($target[0]) { 192 | window.scrollTo(window.scrollX, $target.offset().top - 250); 193 | highlight(); 194 | } 195 | } 196 | 197 | function windowEvents(event) { 198 | var msg = event.data; 199 | if (msg.action === "expand") { 200 | expandTo(msg.path); 201 | } 202 | return false; 203 | } 204 | 205 | window.addEventListener("message", windowEvents, false); 206 | 207 | $(document).ready(function() { 208 | escapeShortcut(); 209 | navResizer(); 210 | enableLinks(); 211 | enableToggles(); 212 | populateSearchCache(); 213 | enableSearch(); 214 | }); 215 | 216 | })(); 217 | -------------------------------------------------------------------------------- /docs/class_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Class List 19 | 20 | 21 | 22 |
23 | 43 | 44 | 49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /docs/Ruff/Standard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Ruff::Standard 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Ruff::Standard 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/ruff/standard.rb
82 |
83 | 84 |
85 | 86 |

Overview

87 |
88 |

Ruff::Standard provides several pre-defined effect handlers modules. 89 | Each module provides Instance class to instantiate and handle the indivisual effect instances.

90 | 91 | 92 |
93 |
94 |
95 | 96 |
97 |

Examples:

98 | 99 | 100 |
include Ruff::Standard
101 | 
102 | state1 = State::Instance.new
103 | state2 = State::Instance.new
104 | 
105 | state1.with_init(3) {
106 | state2.with_init(4) {
107 |   state2.modify {|s| s + state1.get }
108 | 
109 |   puts state1.get #==> 3
110 |   puts state2.get #==> 7
111 | }}
112 | 113 |
114 | 115 | 116 |

Defined Under Namespace

117 |

118 | 119 | 120 | Modules: Async, CurrentTime, Defer, DelimCtrl, MeasureTime, State 121 | 122 | 123 | 124 | 125 |

126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 |
136 | 137 | 142 | 143 |
144 | 145 | -------------------------------------------------------------------------------- /docs/Util/Ref.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Class: Util::Ref 8 | 9 | — Ruff 1.2.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Class: Util::Ref 63 | 64 | 65 | 66 |

67 |
68 | 69 |
70 |
Inherits:
71 |
72 | Object 73 | 74 |
    75 |
  • Object
  • 76 | 77 | 78 | 79 |
80 | show all 81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 |
Defined in:
97 |
lib/ruff/standard/util.rb
98 |
99 | 100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |

111 | Instance Method Summary 112 | collapse 113 |

114 | 115 |
    116 | 117 |
  • 118 | 119 | 120 | #get ⇒ Object 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
    135 | 136 |
  • 137 | 138 | 139 |
  • 140 | 141 | 142 | #initialize(v) ⇒ Ref 143 | 144 | 145 | 146 | 147 | 148 | 149 | constructor 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 |

    A new instance of Ref.

    159 |
    160 | 161 |
  • 162 | 163 | 164 |
  • 165 | 166 | 167 | #set(v_) ⇒ Object 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 |
    182 | 183 |
  • 184 | 185 | 186 |
187 | 188 | 189 |
190 |

Constructor Details

191 | 192 |
193 |

194 | 195 | #initialize(v) ⇒ Ref 196 | 197 | 198 | 199 | 200 | 201 |

202 |
203 |

Returns a new instance of Ref

204 | 205 | 206 |
207 |
208 |
209 | 210 | 211 |
212 | 213 | 221 | 228 | 229 |
214 |
215 | 
216 | 
217 | 26
218 | 27
219 | 28
220 |
222 |
# File 'lib/ruff/standard/util.rb', line 26
223 | 
224 | def initialize(v)
225 |   @v = v
226 | end
227 |
230 |
231 | 232 |
233 | 234 | 235 |
236 |

Instance Method Details

237 | 238 | 239 |
240 |

241 | 242 | #getObject 243 | 244 | 245 | 246 | 247 | 248 |

249 | 250 | 258 | 265 | 266 |
251 |
252 | 
253 | 
254 | 30
255 | 31
256 | 32
257 |
259 |
# File 'lib/ruff/standard/util.rb', line 30
260 | 
261 | def get
262 |   @v
263 | end
264 |
267 |
268 | 269 |
270 |

271 | 272 | #set(v_) ⇒ Object 273 | 274 | 275 | 276 | 277 | 278 |

279 | 280 | 288 | 295 | 296 |
281 |
282 | 
283 | 
284 | 34
285 | 35
286 | 36
287 |
289 |
# File 'lib/ruff/standard/util.rb', line 34
290 | 
291 | def set(v_)
292 |   @v = v_
293 | end
294 |
297 |
298 | 299 |
300 | 301 |
302 | 303 | 308 | 309 |
310 | 311 | -------------------------------------------------------------------------------- /docs/Ruff/Throws/Eff.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Class: Ruff::Throws::Eff 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Class: Ruff::Throws::Eff 63 | 64 | 65 | 66 |

67 |
68 | 69 |
70 |
Inherits:
71 |
72 | Object 73 | 74 |
    75 |
  • Object
  • 76 | 77 | 78 | 79 |
80 | show all 81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 |
Defined in:
97 |
lib/ruff/objects.rb
98 |
99 | 100 |
101 | 102 |

Overview

103 |
104 |

Eff is internal object.

105 | 106 |

They make effects encapsulate with ID and arguments to be sent to the handler.

107 | 108 | 109 |
110 |
111 |
112 | 113 | 114 |
115 | 116 | 117 | 118 |

Instance Attribute Summary collapse

119 |
    120 | 121 |
  • 122 | 123 | 124 | #args ⇒ Object 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | readonly 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 |

    passes to a handler which can catch the effect.

    144 |
    145 | 146 |
  • 147 | 148 | 149 |
  • 150 | 151 | 152 | #id ⇒ Object 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | readonly 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 |

    makes the object unique.

    172 |
    173 | 174 |
  • 175 | 176 | 177 |
178 | 179 | 180 | 181 | 182 | 183 |

184 | Instance Method Summary 185 | collapse 186 |

187 | 188 |
    189 | 190 |
  • 191 | 192 | 193 | #initialize(id, args) ⇒ Eff 194 | 195 | 196 | 197 | 198 | 199 | 200 | constructor 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 |

    creates a new object with id and args.

    210 |
    211 | 212 |
  • 213 | 214 | 215 |
216 | 217 | 218 |
219 |

Constructor Details

220 | 221 |
222 |

223 | 224 | #initialize(id, args) ⇒ Eff 225 | 226 | 227 | 228 | 229 | 230 |

231 |
232 |

creates a new object with id and args.

233 | 234 | 235 |
236 |
237 |
238 | 239 | 240 |
241 | 242 | 251 | 259 | 260 |
243 |
244 | 
245 | 
246 | 16
247 | 17
248 | 18
249 | 19
250 |
252 |
# File 'lib/ruff/objects.rb', line 16
253 | 
254 | def initialize(id, args)
255 |   @id = id
256 |   @args = args
257 | end
258 |
261 |
262 | 263 |
264 | 265 |
266 |

Instance Attribute Details

267 | 268 | 269 | 270 |
271 |

272 | 273 | #argsObject (readonly) 274 | 275 | 276 | 277 | 278 | 279 |

280 |
281 |

passes to a handler which can catch the effect.

282 | 283 | 284 |
285 |
286 |
287 | 288 | 289 |
290 | 291 | 299 | 306 | 307 |
292 |
293 | 
294 | 
295 | 13
296 | 14
297 | 15
298 |
300 |
# File 'lib/ruff/objects.rb', line 13
301 | 
302 | def args
303 |   @args
304 | end
305 |
308 |
309 | 310 | 311 | 312 |
313 |

314 | 315 | #idObject (readonly) 316 | 317 | 318 | 319 | 320 | 321 |

322 |
323 |

makes the object unique.

324 | 325 | 326 |
327 |
328 |
329 | 330 | 331 |
332 | 333 | 341 | 348 | 349 |
334 |
335 | 
336 | 
337 | 10
338 | 11
339 | 12
340 |
342 |
# File 'lib/ruff/objects.rb', line 10
343 | 
344 | def id
345 |   @id
346 | end
347 |
350 |
351 | 352 |
353 | 354 | 355 |
356 | 357 | 362 | 363 |
364 | 365 | -------------------------------------------------------------------------------- /docs/Ruff/Throws/Resend.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Class: Ruff::Throws::Resend 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Class: Ruff::Throws::Resend 63 | 64 | 65 | 66 |

67 |
68 | 69 |
70 |
Inherits:
71 |
72 | Object 73 | 74 |
    75 |
  • Object
  • 76 | 77 | 78 | 79 |
80 | show all 81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 |
Defined in:
97 |
lib/ruff/objects.rb
98 |
99 | 100 |
101 | 102 |

Overview

103 |
104 |

Resend is internal object like Eff.

105 | 106 |

It is used when an effect is unable to be handled and should be thrown to the outer handler.

107 | 108 | 109 |
110 |
111 |
112 | 113 | 114 |
115 | 116 | 117 | 118 |

Instance Attribute Summary collapse

119 |
    120 | 121 |
  • 122 | 123 | 124 | #eff ⇒ Object 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | readonly 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 |

    is abstracted effect (such as Eff or (re)thrown Resend).

    144 |
    145 | 146 |
  • 147 | 148 | 149 |
  • 150 | 151 | 152 | #k ⇒ Object 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | readonly 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 |

    is a continuation of eff thrown context.

    172 |
    173 | 174 |
  • 175 | 176 | 177 |
178 | 179 | 180 | 181 | 182 | 183 |

184 | Instance Method Summary 185 | collapse 186 |

187 | 188 |
    189 | 190 |
  • 191 | 192 | 193 | #initialize(eff, k) ⇒ Resend 194 | 195 | 196 | 197 | 198 | 199 | 200 | constructor 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 |

    creates a new object with eff and k.

    210 |
    211 | 212 |
  • 213 | 214 | 215 |
216 | 217 | 218 |
219 |

Constructor Details

220 | 221 |
222 |

223 | 224 | #initialize(eff, k) ⇒ Resend 225 | 226 | 227 | 228 | 229 | 230 |

231 |
232 |

creates a new object with eff and k.

233 | 234 | 235 |
236 |
237 |
238 | 239 | 240 |
241 | 242 | 251 | 259 | 260 |
243 |
244 | 
245 | 
246 | 33
247 | 34
248 | 35
249 | 36
250 |
252 |
# File 'lib/ruff/objects.rb', line 33
253 | 
254 | def initialize(eff, k)
255 |   @eff = eff
256 |   @k = k
257 | end
258 |
261 |
262 | 263 |
264 | 265 |
266 |

Instance Attribute Details

267 | 268 | 269 | 270 |
271 |

272 | 273 | #effObject (readonly) 274 | 275 | 276 | 277 | 278 | 279 |

280 |
281 |

is abstracted effect (such as Eff or (re)thrown Resend).

282 | 283 | 284 |
285 |
286 |
287 | 288 | 289 |
290 | 291 | 299 | 306 | 307 |
292 |
293 | 
294 | 
295 | 27
296 | 28
297 | 29
298 |
300 |
# File 'lib/ruff/objects.rb', line 27
301 | 
302 | def eff
303 |   @eff
304 | end
305 |
308 |
309 | 310 | 311 | 312 |
313 |

314 | 315 | #kObject (readonly) 316 | 317 | 318 | 319 | 320 | 321 |

322 |
323 |

is a continuation of eff thrown context.

324 | 325 | 326 |
327 |
328 |
329 | 330 | 331 |
332 | 333 | 341 | 348 | 349 |
334 |
335 | 
336 | 
337 | 30
338 | 31
339 | 32
340 |
342 |
# File 'lib/ruff/objects.rb', line 30
343 | 
344 | def k
345 |   @k
346 | end
347 |
350 |
351 | 352 |
353 | 354 | 355 |
356 | 357 | 362 | 363 |
364 | 365 | -------------------------------------------------------------------------------- /docs/Ruff.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Ruff 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Ruff 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/ruff.rb,
82 | lib/ruff/version.rb
83 |
84 |
85 | 86 |
87 | 88 |

Defined Under Namespace

89 |

90 | 91 | 92 | Modules: Standard, Throws 93 | 94 | 95 | 96 | Classes: Effect, Handler 97 | 98 | 99 |

100 | 101 | 102 |

103 | Constant Summary 104 | collapse 105 |

106 | 107 |
108 | 109 |
VERSION = 110 | 111 |
112 |
'2.1.0'
113 | 114 |
115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 |

125 | Class Method Summary 126 | collapse 127 |

128 | 129 |
    130 | 131 |
  • 132 | 133 | 134 | .handler ⇒ Object 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 |

    is alias for Handler.new.

    149 |
    150 | 151 |
  • 152 | 153 | 154 |
  • 155 | 156 | 157 | .instance ⇒ Object 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 |

    is alias for Effect.new.

    172 |
    173 | 174 |
  • 175 | 176 | 177 |
178 | 179 | 180 | 181 | 182 |
183 |

Class Method Details

184 | 185 | 186 |
187 |

188 | 189 | .handlerObject 190 | 191 | 192 | 193 | 194 | 195 |

196 |
197 |

is alias for Handler.new

198 | 199 | 200 |
201 |
202 |
203 | 204 |
205 |

Examples:

206 | 207 | 208 |
log_handler = Ruff.handler.on(Log){|msg, k|
209 |   puts "Logger: #{msg}"
210 |   k[]
211 | }
212 | 213 |
214 | 215 | 216 |

See Also:

217 | 222 | 223 |
224 | 225 | 233 | 240 | 241 |
226 |
227 | 
228 | 
229 | 28
230 | 29
231 | 30
232 |
234 |
# File 'lib/ruff.rb', line 28
235 | 
236 | def handler
237 |   Handler.new
238 | end
239 |
242 |
243 | 244 |
245 |

246 | 247 | .instanceObject 248 | 249 | 250 | 251 | 252 | 253 |

254 |
255 |

is alias for Effect.new

256 | 257 | 258 |
259 |
260 |
261 | 262 |
263 |

Examples:

264 | 265 | 266 |
Log = Ruff.instance #==> Ruff::Effect.new
267 | 268 |
269 | 270 | 271 |

See Also:

272 | 277 | 278 |
279 | 280 | 288 | 295 | 296 |
281 |
282 | 
283 | 
284 | 16
285 | 17
286 | 18
287 |
289 |
# File 'lib/ruff.rb', line 16
290 | 
291 | def instance
292 |   Effect.new
293 | end
294 |
297 |
298 | 299 |
300 | 301 |
302 | 303 | 308 | 309 |
310 | 311 | -------------------------------------------------------------------------------- /docs/Util/FnStack.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Class: Util::FnStack 8 | 9 | — Ruff 1.2.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Class: Util::FnStack 63 | 64 | 65 | 66 |

67 |
68 | 69 |
70 |
Inherits:
71 |
72 | Object 73 | 74 |
    75 |
  • Object
  • 76 | 77 | 78 | 79 |
80 | show all 81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 |
Defined in:
97 |
lib/ruff/standard/util.rb
98 |
99 | 100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |

111 | Instance Method Summary 112 | collapse 113 |

114 | 115 |
    116 | 117 |
  • 118 | 119 | 120 | #cons(hd) ⇒ Object 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
    135 | 136 |
  • 137 | 138 | 139 |
  • 140 | 141 | 142 | #dequeue ⇒ Object 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 |
    157 | 158 |
  • 159 | 160 | 161 |
  • 162 | 163 | 164 | #enqueue(fn) ⇒ Object 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 |
    179 | 180 |
  • 181 | 182 | 183 |
  • 184 | 185 | 186 | #initialize ⇒ FnStack 187 | 188 | 189 | 190 | 191 | 192 | 193 | constructor 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |

    A new instance of FnStack.

    203 |
    204 | 205 |
  • 206 | 207 | 208 |
209 | 210 | 211 |
212 |

Constructor Details

213 | 214 |
215 |

216 | 217 | #initializeFnStack 218 | 219 | 220 | 221 | 222 | 223 |

224 |
225 |

Returns a new instance of FnStack

226 | 227 | 228 |
229 |
230 |
231 | 232 | 233 |
234 | 235 | 243 | 250 | 251 |
236 |
237 | 
238 | 
239 | 6
240 | 7
241 | 8
242 |
244 |
# File 'lib/ruff/standard/util.rb', line 6
245 | 
246 | def initialize
247 |   @queue = []
248 | end
249 |
252 |
253 | 254 |
255 | 256 | 257 |
258 |

Instance Method Details

259 | 260 | 261 |
262 |

263 | 264 | #cons(hd) ⇒ Object 265 | 266 | 267 | 268 | 269 | 270 |

271 | 272 | 281 | 289 | 290 |
273 |
274 | 
275 | 
276 | 19
277 | 20
278 | 21
279 | 22
280 |
282 |
# File 'lib/ruff/standard/util.rb', line 19
283 | 
284 | def cons(hd)
285 |   queue_ = @queue.dup
286 |   queue_.push hd
287 | end
288 |
291 |
292 | 293 |
294 |

295 | 296 | #dequeueObject 297 | 298 | 299 | 300 | 301 | 302 |

303 | 304 | 313 | 321 | 322 |
305 |
306 | 
307 | 
308 | 14
309 | 15
310 | 16
311 | 17
312 |
314 |
# File 'lib/ruff/standard/util.rb', line 14
315 | 
316 | def dequeue
317 |   hd = @queue.pop
318 |   hd[] unless hd.nil?
319 | end
320 |
323 |
324 | 325 |
326 |

327 | 328 | #enqueue(fn) ⇒ Object 329 | 330 | 331 | 332 | 333 | 334 |

335 | 336 | 344 | 351 | 352 |
337 |
338 | 
339 | 
340 | 10
341 | 11
342 | 12
343 |
345 |
# File 'lib/ruff/standard/util.rb', line 10
346 | 
347 | def enqueue(fn)
348 |   @queue.push fn
349 | end
350 |
353 |
354 | 355 |
356 | 357 |
358 | 359 | 364 | 365 |
366 | 367 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | File: README 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 59 | 60 |

ruff

61 | 62 |

Gem Version

63 | 64 | 68 | 69 |

ONE-SHOT Algebraic Effects Library for Ruby!

70 | 71 |
require "ruff"
 72 | 
 73 | Double = Ruff.instance
 74 | Triple = Ruff.instance
 75 | Log = Ruff.instance
 76 | 
 77 | h1 = Ruff.handler
 78 |   .on(Double){|k, v| k[v * 2] }
 79 |   .on(Triple){|k, v| k[v * 3] }
 80 | 
 81 | h2 = Ruff.handler.on(Log){|k, msg|
 82 |   k[]
 83 |   puts "logger: #{msg}"
 84 | }
 85 | 
 86 | h1.run{
 87 |   h2.run{
 88 |     v = Double.perform 2
 89 |     Log.perform (v + 2)
 90 |     puts Triple.perform 3
 91 |   }
 92 | }
 93 | # ==> prints
 94 | # 9
 95 | # logger: 6
 96 | 
97 | 98 |

Feature

99 | 100 |

One-shot algebraic effects

101 | 102 |

You can access the delimited continuation which can run only once. 103 | Even the limitation exists, you can write powerful control flow manipulation, like async/await, call1cc.

104 | 105 |

We have an formal definition for the implementation, by showing a conversion from algebraic effects and handlers to asymmetric coroutines. 106 | See here (in Japanese).

107 | 108 |

Subtyping on effects

109 | 110 |

You can define an sub effect for another effect. 111 | It enables to make an effect hierarchy, such as Exceptions in Java. 112 | This implementation is based on a calculus λσ<: (Description (in Japanese)).

113 | 114 |

Pre-defined effect and handlers

115 | 116 |

We provide some ready-to-use effect and handlers. 117 | You can use quickly powerful control flows.

118 | 119 | 127 | 128 |

LICENSE

129 | 130 |

MIT

131 |
132 | 133 | 138 | 139 |
140 | 141 | -------------------------------------------------------------------------------- /docs/_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Ruff 2.1.0 Documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 |
34 | 54 | 55 |

Ruff 2.1.0 Documentation

56 |
57 |

Alphabetic Index

58 | 59 |

File Listing

60 |
    61 | 62 | 63 |
  • README
  • 64 | 65 | 66 |
67 | 68 |
69 |

Namespace Listing A-Z

70 | 71 | 72 | 73 | 74 | 75 | 76 | 286 | 287 |
77 | 78 | 79 |
    80 |
  • A
  • 81 |
      82 | 83 |
    • 84 | Async 85 | 86 | (Ruff::Standard) 87 | 88 |
    • 89 | 90 |
    91 |
92 | 93 | 94 |
    95 |
  • C
  • 96 |
      97 | 98 |
    • 99 | CurrentTime 100 | 101 | (Ruff::Standard) 102 | 103 |
    • 104 | 105 |
    106 |
107 | 108 | 109 |
    110 |
  • D
  • 111 |
      112 | 113 |
    • 114 | Defer 115 | 116 | (Ruff::Standard) 117 | 118 |
    • 119 | 120 |
    • 121 | DelimCtrl 122 | 123 | (Ruff::Standard) 124 | 125 |
    • 126 | 127 |
    128 |
129 | 130 | 131 |
    132 |
  • E
  • 133 |
      134 | 135 |
    • 136 | Eff 137 | 138 | (Ruff::Throws) 139 | 140 |
    • 141 | 142 |
    • 143 | Effect 144 | 145 | (Ruff) 146 | 147 |
    • 148 | 149 |
    150 |
151 | 152 | 153 |
    154 |
  • H
  • 155 |
      156 | 157 |
    • 158 | Handler 159 | 160 | (Ruff) 161 | 162 |
    • 163 | 164 |
    165 |
166 | 167 | 168 |
    169 |
  • I
  • 170 |
      171 | 172 |
    • 173 | Instance 174 | 175 | (Ruff::Standard::Async) 176 | 177 |
    • 178 | 179 |
    • 180 | Instance 181 | 182 | (Ruff::Standard::CurrentTime) 183 | 184 |
    • 185 | 186 |
    • 187 | Instance 188 | 189 | (Ruff::Standard::Defer) 190 | 191 |
    • 192 | 193 |
    • 194 | Instance 195 | 196 | (Ruff::Standard::MeasureTime) 197 | 198 |
    • 199 | 200 |
    • 201 | Instance 202 | 203 | (Ruff::Standard::State) 204 | 205 |
    • 206 | 207 |
    208 |
209 | 210 | 211 |
    212 |
  • M
  • 213 |
      214 | 215 |
    • 216 | MeasureTime 217 | 218 | (Ruff::Standard) 219 | 220 |
    • 221 | 222 |
    223 |
224 | 225 | 226 |
227 | 228 | 229 |
    230 |
  • R
  • 231 |
      232 | 233 |
    • 234 | Resend 235 | 236 | (Ruff::Throws) 237 | 238 |
    • 239 | 240 |
    • 241 | Ruff 242 | 243 |
    • 244 | 245 |
    246 |
247 | 248 | 249 |
    250 |
  • S
  • 251 |
      252 | 253 |
    • 254 | Standard 255 | 256 | (Ruff) 257 | 258 |
    • 259 | 260 |
    • 261 | State 262 | 263 | (Ruff::Standard) 264 | 265 |
    • 266 | 267 |
    268 |
269 | 270 | 271 |
    272 |
  • T
  • 273 |
      274 | 275 |
    • 276 | Throws 277 | 278 | (Ruff) 279 | 280 |
    • 281 | 282 |
    283 |
284 | 285 |
288 | 289 |
290 | 291 |
292 | 293 | 298 | 299 |
300 | 301 | -------------------------------------------------------------------------------- /docs/file.README.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | File: README 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 59 | 60 |

ruff

61 | 62 |

Gem Version

63 | 64 | 68 | 69 |

ONE-SHOT Algebraic Effects Library for Ruby!

70 | 71 |
require "ruff"
 72 | 
 73 | Double = Ruff.instance
 74 | Triple = Ruff.instance
 75 | Log = Ruff.instance
 76 | 
 77 | h1 = Ruff.handler
 78 |   .on(Double){|k, v| k[v * 2] }
 79 |   .on(Triple){|k, v| k[v * 3] }
 80 | 
 81 | h2 = Ruff.handler.on(Log){|k, msg|
 82 |   k[]
 83 |   puts "logger: #{msg}"
 84 | }
 85 | 
 86 | h1.run{
 87 |   h2.run{
 88 |     v = Double.perform 2
 89 |     Log.perform (v + 2)
 90 |     puts Triple.perform 3
 91 |   }
 92 | }
 93 | # ==> prints
 94 | # 9
 95 | # logger: 6
 96 | 
97 | 98 |

Feature

99 | 100 |

One-shot algebraic effects

101 | 102 |

You can access the delimited continuation which can run only once. 103 | Even the limitation exists, you can write powerful control flow manipulation, like async/await, call1cc.

104 | 105 |

We have an formal definition for the implementation, by showing a conversion from algebraic effects and handlers to asymmetric coroutines. 106 | See here (in Japanese).

107 | 108 |

Subtyping on effects

109 | 110 |

You can define an sub effect for another effect. 111 | It enables to make an effect hierarchy, such as Exceptions in Java. 112 | This implementation is based on a calculus λσ<: (Description (in Japanese)).

113 | 114 |

Pre-defined effect and handlers

115 | 116 |

We provide some ready-to-use effect and handlers. 117 | You can use quickly powerful control flows.

118 | 119 | 127 | 128 |

LICENSE

129 | 130 |

MIT

131 |
132 | 133 | 138 | 139 |
140 | 141 | -------------------------------------------------------------------------------- /docs/Ruff/Standard/CurrentTime.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Ruff::Standard::CurrentTime 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Ruff::Standard::CurrentTime 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/ruff/standard/current_time.rb
82 |
83 | 84 |
85 | 86 |

Overview

87 |
88 |

CurrentTime provides an effect CurrentTime.eff and the implementation returning Time.now .

89 | 90 |

The module has an instance of Instance and provides its methods as module method.

91 | 92 | 93 |
94 |
95 |
96 | 97 | 98 |

See Also:

99 | 104 | 105 |

Defined Under Namespace

106 |

107 | 108 | 109 | 110 | 111 | Classes: Instance 112 | 113 | 114 |

115 | 116 | 117 | 118 | 119 |

Instance Attribute Summary collapse

120 |
    121 | 122 |
  • 123 | 124 | 125 | #eff ⇒ Object 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | readonly 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
    145 | 146 |
  • 147 | 148 | 149 |
150 | 151 | 152 | 153 | 154 | 155 |

156 | Class Method Summary 157 | collapse 158 |

159 | 160 |
    161 | 162 |
  • 163 | 164 | 165 | .get ⇒ Object 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 |
    180 | 181 |
  • 182 | 183 | 184 |
  • 185 | 186 | 187 | .with(&th) ⇒ Object 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 |
    202 | 203 |
  • 204 | 205 | 206 |
207 | 208 | 209 | 210 |
211 |

Instance Attribute Details

212 | 213 | 214 | 215 |
216 |

217 | 218 | #effObject (readonly) 219 | 220 | 221 | 222 | 223 | 224 |

225 |
226 | 227 | 228 |
229 |
230 |
231 | 232 | 233 |

See Also:

234 | 239 | 240 |
241 | 242 | 250 | 257 | 258 |
243 |
244 | 
245 | 
246 | 57
247 | 58
248 | 59
249 |
251 |
# File 'lib/ruff/standard/current_time.rb', line 57
252 | 
253 | def eff
254 |   @eff
255 | end
256 |
259 |
260 | 261 |
262 | 263 | 264 |
265 |

Class Method Details

266 | 267 | 268 |
269 |

270 | 271 | .getObject 272 | 273 | 274 | 275 | 276 | 277 |

278 |
279 | 280 | 281 |
282 |
283 |
284 | 285 | 286 |

See Also:

287 | 292 | 293 |
294 | 295 | 303 | 310 | 311 |
296 |
297 | 
298 | 
299 | 45
300 | 46
301 | 47
302 |
304 |
# File 'lib/ruff/standard/current_time.rb', line 45
305 | 
306 | def get
307 |   @inst.get
308 | end
309 |
312 |
313 | 314 |
315 |

316 | 317 | .with(&th) ⇒ Object 318 | 319 | 320 | 321 | 322 | 323 |

324 |
325 | 326 | 327 |
328 |
329 |
330 | 331 | 332 |

See Also:

333 | 338 | 339 |
340 | 341 | 349 | 356 | 357 |
342 |
343 | 
344 | 
345 | 50
346 | 51
347 | 52
348 |
350 |
# File 'lib/ruff/standard/current_time.rb', line 50
351 | 
352 | def with(&th)
353 |   @inst.with(&th)
354 | end
355 |
358 |
359 | 360 |
361 | 362 |
363 | 364 | 369 | 370 |
371 | 372 | -------------------------------------------------------------------------------- /docs/Ruff/Standard/Defer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Ruff::Standard::Defer 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Ruff::Standard::Defer 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/ruff/standard/defer.rb
82 |
83 | 84 |
85 | 86 |

Overview

87 |
88 |

Defer provides effects Defer.eff , 89 | and the implementation to defer procedures .

90 | 91 |

The module has an instance of Instance and provides its methods as module method.

92 | 93 | 94 |
95 |
96 |
97 | 98 | 99 |

See Also:

100 | 105 | 106 |

Defined Under Namespace

107 |

108 | 109 | 110 | 111 | 112 | Classes: Instance 113 | 114 | 115 |

116 | 117 | 118 | 119 | 120 |

Instance Attribute Summary collapse

121 |
    122 | 123 |
  • 124 | 125 | 126 | #eff ⇒ Object 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | readonly 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
    146 | 147 |
  • 148 | 149 | 150 |
151 | 152 | 153 | 154 | 155 | 156 |

157 | Class Method Summary 158 | collapse 159 |

160 | 161 |
    162 | 163 |
  • 164 | 165 | 166 | .register(&prc) ⇒ Object 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 |
    181 | 182 |
  • 183 | 184 | 185 |
  • 186 | 187 | 188 | .with(&th) ⇒ Object 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
    203 | 204 |
  • 205 | 206 | 207 |
208 | 209 | 210 | 211 |
212 |

Instance Attribute Details

213 | 214 | 215 | 216 |
217 |

218 | 219 | #effObject (readonly) 220 | 221 | 222 | 223 | 224 | 225 |

226 |
227 | 228 | 229 |
230 |
231 |
232 | 233 | 234 |

See Also:

235 | 240 | 241 |
242 | 243 | 251 | 258 | 259 |
244 |
245 | 
246 | 
247 | 72
248 | 73
249 | 74
250 |
252 |
# File 'lib/ruff/standard/defer.rb', line 72
253 | 
254 | def eff
255 |   @eff
256 | end
257 |
260 |
261 | 262 |
263 | 264 | 265 |
266 |

Class Method Details

267 | 268 | 269 |
270 |

271 | 272 | .register(&prc) ⇒ Object 273 | 274 | 275 | 276 | 277 | 278 |

279 |
280 | 281 | 282 |
283 |
284 |
285 | 286 | 287 |

See Also:

288 | 293 | 294 |
295 | 296 | 304 | 311 | 312 |
297 |
298 | 
299 | 
300 | 60
301 | 61
302 | 62
303 |
305 |
# File 'lib/ruff/standard/defer.rb', line 60
306 | 
307 | def register(&prc)
308 |   @inst.register(&prc)
309 | end
310 |
313 |
314 | 315 |
316 |

317 | 318 | .with(&th) ⇒ Object 319 | 320 | 321 | 322 | 323 | 324 |

325 |
326 | 327 | 328 |
329 |
330 |
331 | 332 | 333 |

See Also:

334 | 339 | 340 |
341 | 342 | 350 | 357 | 358 |
343 |
344 | 
345 | 
346 | 65
347 | 66
348 | 67
349 |
351 |
# File 'lib/ruff/standard/defer.rb', line 65
352 | 
353 | def with(&th)
354 |   @inst.with(&th)
355 | end
356 |
359 |
360 | 361 |
362 | 363 |
364 | 365 | 370 | 371 |
372 | 373 | -------------------------------------------------------------------------------- /docs/js/app.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var localStorage = {}, sessionStorage = {}; 4 | try { localStorage = window.localStorage; } catch (e) { } 5 | try { sessionStorage = window.sessionStorage; } catch (e) { } 6 | 7 | function createSourceLinks() { 8 | $('.method_details_list .source_code'). 9 | before("[View source]"); 10 | $('.toggleSource').toggle(function() { 11 | $(this).parent().nextAll('.source_code').slideDown(100); 12 | $(this).text("Hide source"); 13 | }, 14 | function() { 15 | $(this).parent().nextAll('.source_code').slideUp(100); 16 | $(this).text("View source"); 17 | }); 18 | } 19 | 20 | function createDefineLinks() { 21 | var tHeight = 0; 22 | $('.defines').after(" more..."); 23 | $('.toggleDefines').toggle(function() { 24 | tHeight = $(this).parent().prev().height(); 25 | $(this).prev().css('display', 'inline'); 26 | $(this).parent().prev().height($(this).parent().height()); 27 | $(this).text("(less)"); 28 | }, 29 | function() { 30 | $(this).prev().hide(); 31 | $(this).parent().prev().height(tHeight); 32 | $(this).text("more..."); 33 | }); 34 | } 35 | 36 | function createFullTreeLinks() { 37 | var tHeight = 0; 38 | $('.inheritanceTree').toggle(function() { 39 | tHeight = $(this).parent().prev().height(); 40 | $(this).parent().toggleClass('showAll'); 41 | $(this).text("(hide)"); 42 | $(this).parent().prev().height($(this).parent().height()); 43 | }, 44 | function() { 45 | $(this).parent().toggleClass('showAll'); 46 | $(this).parent().prev().height(tHeight); 47 | $(this).text("show all"); 48 | }); 49 | } 50 | 51 | function searchFrameButtons() { 52 | $('.full_list_link').click(function() { 53 | toggleSearchFrame(this, $(this).attr('href')); 54 | return false; 55 | }); 56 | window.addEventListener('message', function(e) { 57 | if (e.data === 'navEscape') { 58 | $('#nav').slideUp(100); 59 | $('#search a').removeClass('active inactive'); 60 | $(window).focus(); 61 | } 62 | }); 63 | 64 | $(window).resize(function() { 65 | if ($('#search:visible').length === 0) { 66 | $('#nav').removeAttr('style'); 67 | $('#search a').removeClass('active inactive'); 68 | $(window).focus(); 69 | } 70 | }); 71 | } 72 | 73 | function toggleSearchFrame(id, link) { 74 | var frame = $('#nav'); 75 | $('#search a').removeClass('active').addClass('inactive'); 76 | if (frame.attr('src') === link && frame.css('display') !== "none") { 77 | frame.slideUp(100); 78 | $('#search a').removeClass('active inactive'); 79 | } 80 | else { 81 | $(id).addClass('active').removeClass('inactive'); 82 | if (frame.attr('src') !== link) frame.attr('src', link); 83 | frame.slideDown(100); 84 | } 85 | } 86 | 87 | function linkSummaries() { 88 | $('.summary_signature').click(function() { 89 | document.location = $(this).find('a').attr('href'); 90 | }); 91 | } 92 | 93 | function summaryToggle() { 94 | $('.summary_toggle').click(function(e) { 95 | e.preventDefault(); 96 | localStorage.summaryCollapsed = $(this).text(); 97 | $('.summary_toggle').each(function() { 98 | $(this).text($(this).text() == "collapse" ? "expand" : "collapse"); 99 | var next = $(this).parent().parent().nextAll('ul.summary').first(); 100 | if (next.hasClass('compact')) { 101 | next.toggle(); 102 | next.nextAll('ul.summary').first().toggle(); 103 | } 104 | else if (next.hasClass('summary')) { 105 | var list = $('
    '); 106 | list.html(next.html()); 107 | list.find('.summary_desc, .note').remove(); 108 | list.find('a').each(function() { 109 | $(this).html($(this).find('strong').html()); 110 | $(this).parent().html($(this)[0].outerHTML); 111 | }); 112 | next.before(list); 113 | next.toggle(); 114 | } 115 | }); 116 | return false; 117 | }); 118 | if (localStorage.summaryCollapsed == "collapse") { 119 | $('.summary_toggle').first().click(); 120 | } else { localStorage.summaryCollapsed = "expand"; } 121 | } 122 | 123 | function constantSummaryToggle() { 124 | $('.constants_summary_toggle').click(function(e) { 125 | e.preventDefault(); 126 | localStorage.summaryCollapsed = $(this).text(); 127 | $('.constants_summary_toggle').each(function() { 128 | $(this).text($(this).text() == "collapse" ? "expand" : "collapse"); 129 | var next = $(this).parent().parent().nextAll('dl.constants').first(); 130 | if (next.hasClass('compact')) { 131 | next.toggle(); 132 | next.nextAll('dl.constants').first().toggle(); 133 | } 134 | else if (next.hasClass('constants')) { 135 | var list = $('
    '); 136 | list.html(next.html()); 137 | list.find('dt').each(function() { 138 | $(this).addClass('summary_signature'); 139 | $(this).text( $(this).text().split('=')[0]); 140 | if ($(this).has(".deprecated").length) { 141 | $(this).addClass('deprecated'); 142 | }; 143 | }); 144 | // Add the value of the constant as "Tooltip" to the summary object 145 | list.find('pre.code').each(function() { 146 | console.log($(this).parent()); 147 | var dt_element = $(this).parent().prev(); 148 | var tooltip = $(this).text(); 149 | if (dt_element.hasClass("deprecated")) { 150 | tooltip = 'Deprecated. ' + tooltip; 151 | }; 152 | dt_element.attr('title', tooltip); 153 | }); 154 | list.find('.docstring, .tags, dd').remove(); 155 | next.before(list); 156 | next.toggle(); 157 | } 158 | }); 159 | return false; 160 | }); 161 | if (localStorage.summaryCollapsed == "collapse") { 162 | $('.constants_summary_toggle').first().click(); 163 | } else { localStorage.summaryCollapsed = "expand"; } 164 | } 165 | 166 | function generateTOC() { 167 | if ($('#filecontents').length === 0) return; 168 | var _toc = $('
      '); 169 | var show = false; 170 | var toc = _toc; 171 | var counter = 0; 172 | var tags = ['h2', 'h3', 'h4', 'h5', 'h6']; 173 | var i; 174 | var curli; 175 | if ($('#filecontents h1').length > 1) tags.unshift('h1'); 176 | for (i = 0; i < tags.length; i++) { tags[i] = '#filecontents ' + tags[i]; } 177 | var lastTag = parseInt(tags[0][1], 10); 178 | $(tags.join(', ')).each(function() { 179 | if ($(this).parents('.method_details .docstring').length != 0) return; 180 | if (this.id == "filecontents") return; 181 | show = true; 182 | var thisTag = parseInt(this.tagName[1], 10); 183 | if (this.id.length === 0) { 184 | var proposedId = $(this).attr('toc-id'); 185 | if (typeof(proposedId) != "undefined") this.id = proposedId; 186 | else { 187 | var proposedId = $(this).text().replace(/[^a-z0-9-]/ig, '_'); 188 | if ($('#' + proposedId).length > 0) { proposedId += counter; counter++; } 189 | this.id = proposedId; 190 | } 191 | } 192 | if (thisTag > lastTag) { 193 | for (i = 0; i < thisTag - lastTag; i++) { 194 | if ( typeof(curli) == "undefined" ) { 195 | curli = $('
    1. '); 196 | toc.append(curli); 197 | } 198 | toc = $('
        '); 199 | curli.append(toc); 200 | curli = undefined; 201 | } 202 | } 203 | if (thisTag < lastTag) { 204 | for (i = 0; i < lastTag - thisTag; i++) { 205 | toc = toc.parent(); 206 | toc = toc.parent(); 207 | } 208 | } 209 | var title = $(this).attr('toc-title'); 210 | if (typeof(title) == "undefined") title = $(this).text(); 211 | curli =$('
      1. ' + title + '
      2. '); 212 | toc.append(curli); 213 | lastTag = thisTag; 214 | }); 215 | if (!show) return; 216 | html = ''; 217 | $('#content').prepend(html); 218 | $('#toc').append(_toc); 219 | $('#toc .hide_toc').toggle(function() { 220 | $('#toc .top').slideUp('fast'); 221 | $('#toc').toggleClass('hidden'); 222 | $('#toc .title small').toggle(); 223 | }, function() { 224 | $('#toc .top').slideDown('fast'); 225 | $('#toc').toggleClass('hidden'); 226 | $('#toc .title small').toggle(); 227 | }); 228 | } 229 | 230 | function navResizeFn(e) { 231 | if (e.which !== 1) { 232 | navResizeFnStop(); 233 | return; 234 | } 235 | 236 | sessionStorage.navWidth = e.pageX.toString(); 237 | $('.nav_wrap').css('width', e.pageX); 238 | $('.nav_wrap').css('-ms-flex', 'inherit'); 239 | } 240 | 241 | function navResizeFnStop() { 242 | $(window).unbind('mousemove', navResizeFn); 243 | window.removeEventListener('message', navMessageFn, false); 244 | } 245 | 246 | function navMessageFn(e) { 247 | if (e.data.action === 'mousemove') navResizeFn(e.data.event); 248 | if (e.data.action === 'mouseup') navResizeFnStop(); 249 | } 250 | 251 | function navResizer() { 252 | $('#resizer').mousedown(function(e) { 253 | e.preventDefault(); 254 | $(window).mousemove(navResizeFn); 255 | window.addEventListener('message', navMessageFn, false); 256 | }); 257 | $(window).mouseup(navResizeFnStop); 258 | 259 | if (sessionStorage.navWidth) { 260 | navResizeFn({which: 1, pageX: parseInt(sessionStorage.navWidth, 10)}); 261 | } 262 | } 263 | 264 | function navExpander() { 265 | var done = false, timer = setTimeout(postMessage, 500); 266 | function postMessage() { 267 | if (done) return; 268 | clearTimeout(timer); 269 | var opts = { action: 'expand', path: pathId }; 270 | document.getElementById('nav').contentWindow.postMessage(opts, '*'); 271 | done = true; 272 | } 273 | 274 | window.addEventListener('message', function(event) { 275 | if (event.data === 'navReady') postMessage(); 276 | return false; 277 | }, false); 278 | } 279 | 280 | function mainFocus() { 281 | var hash = window.location.hash; 282 | if (hash !== '' && $(hash)[0]) { 283 | $(hash)[0].scrollIntoView(); 284 | } 285 | 286 | setTimeout(function() { $('#main').focus(); }, 10); 287 | } 288 | 289 | function navigationChange() { 290 | // This works around the broken anchor navigation with the YARD template. 291 | window.onpopstate = function() { 292 | var hash = window.location.hash; 293 | if (hash !== '' && $(hash)[0]) { 294 | $(hash)[0].scrollIntoView(); 295 | } 296 | }; 297 | } 298 | 299 | $(document).ready(function() { 300 | navResizer(); 301 | navExpander(); 302 | createSourceLinks(); 303 | createDefineLinks(); 304 | createFullTreeLinks(); 305 | searchFrameButtons(); 306 | linkSummaries(); 307 | summaryToggle(); 308 | constantSummaryToggle(); 309 | generateTOC(); 310 | mainFocus(); 311 | navigationChange(); 312 | }); 313 | 314 | })(); 315 | -------------------------------------------------------------------------------- /docs/Ruff/Standard/MeasureTime.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Ruff::Standard::MeasureTime 8 | 9 | — Ruff 2.1.0 Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
        36 | 61 | 62 |

        Module: Ruff::Standard::MeasureTime 63 | 64 | 65 | 66 |

        67 |
        68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
        80 |
        Defined in:
        81 |
        lib/ruff/standard/measure_time.rb
        82 |
        83 | 84 |
        85 | 86 |

        Overview

        87 |
        88 |

        MeasureTime provides effects MeasureTime.eff , 89 | and the implementation to measure and report execution time .

        90 | 91 |

        The module has an instance of Instance and provides its methods as module method.

        92 | 93 | 94 |
        95 |
        96 |
        97 | 98 |
        99 |

        Examples:

        100 | 101 | 102 |
        MeasureTime.with {
        103 |   MeasureTime.measure 'one'
        104 |   sleep 1
        105 |   MeasureTime.measure 'two'
        106 |   sleep 0.1
        107 | 
        108 |   return 0
        109 | }
        110 | #==> [0, {:label=>"two", :time=>0.1}, {:label=>"one", :time=>1.1}]
        111 | 112 |
        113 | 114 | 115 |

        See Also:

        116 | 121 | 122 |

        Defined Under Namespace

        123 |

        124 | 125 | 126 | 127 | 128 | Classes: Instance 129 | 130 | 131 |

        132 | 133 | 134 | 135 | 136 |

        Instance Attribute Summary collapse

        137 |
          138 | 139 |
        • 140 | 141 | 142 | #eff ⇒ Object 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | readonly 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 |
          162 | 163 |
        • 164 | 165 | 166 |
        167 | 168 | 169 | 170 | 171 | 172 |

        173 | Class Method Summary 174 | collapse 175 |

        176 | 177 |
          178 | 179 |
        • 180 | 181 | 182 | .measure(label) ⇒ Object 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 |
          197 | 198 |
        • 199 | 200 | 201 |
        • 202 | 203 | 204 | .with(&th) ⇒ Object 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 |
          219 | 220 |
        • 221 | 222 | 223 |
        224 | 225 | 226 | 227 |
        228 |

        Instance Attribute Details

        229 | 230 | 231 | 232 |
        233 |

        234 | 235 | #effObject (readonly) 236 | 237 | 238 | 239 | 240 | 241 |

        242 |
        243 | 244 | 245 |
        246 |
        247 |
        248 | 249 | 250 |

        See Also:

        251 | 256 | 257 |
        258 | 259 | 267 | 274 | 275 |
        260 |
        261 | 
        262 | 
        263 | 76
        264 | 77
        265 | 78
        266 |
        268 |
        # File 'lib/ruff/standard/measure_time.rb', line 76
        269 | 
        270 | def eff
        271 |   @eff
        272 | end
        273 |
        276 |
        277 | 278 |
        279 | 280 | 281 |
        282 |

        Class Method Details

        283 | 284 | 285 |
        286 |

        287 | 288 | .measure(label) ⇒ Object 289 | 290 | 291 | 292 | 293 | 294 |

        295 |
        296 | 297 | 298 |
        299 |
        300 |
        301 | 302 | 303 |

        See Also:

        304 | 309 | 310 |
        311 | 312 | 320 | 327 | 328 |
        313 |
        314 | 
        315 | 
        316 | 64
        317 | 65
        318 | 66
        319 |
        321 |
        # File 'lib/ruff/standard/measure_time.rb', line 64
        322 | 
        323 | def measure(label)
        324 |   @inst.measure(label)
        325 | end
        326 |
        329 |
        330 | 331 |
        332 |

        333 | 334 | .with(&th) ⇒ Object 335 | 336 | 337 | 338 | 339 | 340 |

        341 |
        342 | 343 | 344 |
        345 |
        346 |
        347 | 348 | 349 |

        See Also:

        350 | 355 | 356 |
        357 | 358 | 366 | 373 | 374 |
        359 |
        360 | 
        361 | 
        362 | 69
        363 | 70
        364 | 71
        365 |
        367 |
        # File 'lib/ruff/standard/measure_time.rb', line 69
        368 | 
        369 | def with(&th)
        370 |   @inst.with(&th)
        371 | end
        372 |
        375 |
        376 | 377 |
        378 | 379 |
        380 | 381 | 386 | 387 |
        388 | 389 | --------------------------------------------------------------------------------