Search code examples
argumentsblocksmalltalkfunction-composition

Smalltalk: using a block as a block argument?


Is it possible to pass a block statement as a block argument for a given depth k? For example, if k = 1, I have f(x) defined as [x + 3], but if k=2, I want f(f(x)), which is [[x + 3] + 3]. This is for an assignment, so I am required to use a block statement. #value: seems to only take integers, is there a more general version of #value:?

EDIT: Let me be a little clearer. My function takes a range from a to b, and a depth k. The function will evaluate on a block statement. So, for [x + 3] method: 1 to: 3 for: 2 will evaluate [[x+3] + 3] from 1 to 3. I can evaluate from 1 to 3 just fine whenever k = 1. I'm trying to modify the block statement [x+3] into [[x+3]+3], and that's where I'm running into trouble. I've tried

tempvariable := self "where self is the block statement [x+3]"
tempvariable := tempvariable value: self

but I'm getting a BlockClosure(Object)>>doesNotUnderstand: #adaptToNumber:andSend: error (which is why I thought it was integer only)


Solution

  • If I understand your question correctly, you want a method (you named it #method:to:for:) that creates and evaluates a block based on an initial block.

    As in

    [:x | x + 3] method: 1 to: 3 for: 1. 
    

    becomes

    1 to: 3 do: [:index| [:x | x + 3] value: index].
    

    and evaluates [:x | x + 3] three times; and

    [:x | x + 3] method: 1 to: 3 for: 2. 
    

    becomes

    1 to: 3 do: [:index| [:y | ([:x | x + 3] value: y) + 3] value: index].
    

    It is not possible to achieve this by modifying an existing block. However, we can try to solve that problem in another way.

    Let's write your function and block differently:

    function                           block                    
    f(x) = x + 3                       f := [:x | x + 3]. 
         ↪ f(1) = 4                        ↪ f value: 1 "=> 4"
    
    f₁(x) = f(x)                       f1 := [:x | f value: x].
         ↪ f₁(1) = f(1) = 4                ↪ f1 value: 1 "=> 4"
    
    f₂(x) = f(f(x)) = f₁(f(x))         f2 := [:x | f1 value: (f value: x)].
         ↪ f₂(1) = f₁(4) = 7               ↪ f2 value: 1 "=> 7"
    
    f₃(x) = f(f(f(x))) = f₂(f(x))      f3 := [:x | f2 value: (f value: x)].
         ↪ f₃(1) = f₂(4) = f₁(7) = 10       ↪ f3 value: 1 "=> 10"
    

    For depth k, just invoke the block of k-1 on the result of the original block.


    Solution

    So, a solution can look like

    BlockClosure>>depth: k
        | closure |
        closure := self.
        k timesRepeat: [ | newClosure |
            newClosure := closure.
            closure := [:x | newClosure value: (self value: x)]].
        ^ closure
    

    and you can invoke it like this:

    ([:x | x + 3] depth: 0) value: 1. "=> 4"
    ([:x | x + 3] depth: 1) value: 1. "=> 7"
    ([:x | x + 3] depth: 2) value: 1. "=> 10"
    

    Adapt to your needs.