[spoiler warning]
> The *puzzles* are
>
> 1. define "curry2" and "curry3" words
> 2. define "curry3" via "curry2"
> 3. define general "curry" word ( xt u -- xt1 )
>
>
>
> In Haskell any function is already curried.
> Conceptually (not using Haskell syntax):
> f(a)(b) === f(a,b)
> f(a)(b)(c) === f(a,b,c)
>
> In ECMAScript:
> let curry2 = f => a => b => f(a, b);
> let curry3 = f => a => b => c => f(a, b, c);
>
> curry3( (a,b,c) => (a*b+c) )(2)(3)(4) === 10
>
>
>
> \ testcase for Forth
>
> : a ( xt x1 -- x ) swap execute ;
>
> : muladd * + ; ' muladd curry3 2 a 3 a 4 a . cr \ prints 10
>
One of the possible solution is following.
\ some helpers
: =? ( x x1 -- true | x false ) over = dup if nip then ;
: ?E ( flag -- ) postpone if postpone exit postpone then ; immediate
\ constant
: k ( x -- xt ) p{ lit{} }p ;
\ composition of two functions
: co ( xt2 xt1 -- xt ) p{ call{} call{} }p ;
\ The two above words can be easily defined in bare ANS Forth
\ via ':noname' but just slightly longer.
\ In their turn, p{ ... }p are defined in ANS Forth too.
\ reverse composition of two functions
: rco ( xt1 xt2 -- xt ) swap co ;
: partial1 ( 1*x xt -- xt1 ) swap k co ;
: curry2 ( xt -- xt1 ) 'partial1 partial1 ;
: curry3 ( xt -- xt1 ) curry2 'curry2 rco ;
: curry4 ( xt -- xt1 ) curry2 'curry3 rco ;
: curry5 ( xt -- xt1 ) curry2 'curry4 rco ;
: curry ( xt u -- xt1 ) 1- 0 =? ?E itself partial1 swap curry2 co ;
\ 'itself' cannot be defined in ANS Forth.
\ A possible workaround:
: partial2 ( 2*x xt -- xt1 ) partial1 partial1 ;
: curry ( xt u -- xt1 )
p{ ( xt it u -- xt1 )
1- 0 =? if drop exit then
over partial2 swap curry2 co
}p tuck execute \ a kind of Y combinator
;
\ Another workaround:
0 VALUE xt-of-curry
: curry ( xt u -- xt1 ) 1- 0 =? ?E xt-of-curry partial1 swap curry2 co ;
' curry TO xt-of-curry
=== *How to find this solution*
== *A brute force solution* for puzzle 1
: curry2
p{
p{ lit{} call{ lit{} }call }p
}p
;
: curry3
p{ p{
p{ lit{} lit{ lit{} }lit call{ lit{ lit{} }lit }call }p
}p }p
;
\ these definitions can be tested
\ using the following Forth extension:
\
https://github.com/ruv/forth-design-exp/blob/master/lexeme-translator/advanced.example.fth
== *Simplification* of curry2
The above definition of curry2 is equivalent to (⇔)
\ (1.1)
: curry2
p{
lit{} swap p{ lit{} call{} }p
}p
;
⇔ \ (1.2) factor out partial1 function
: partial1 ( x xt -- xt1 ) swap p{ lit{} call{} }p ;
: curry2 p{ lit{} partial1 }p ;
⇔ \ (1.3) lift up partial1
: curry2 'partial1 swap p{ lit{} call{} }p ;
⇔ \ (1.4) substitute partial1 in place of its body
: curry2 'partial1 partial1 ;
partial1 can be also expressed via more fundamental combinators
\ constant
: k ( x -- xt ) p{ lit{} }p ;
\ composition of two functions
: co ( xt2 xt1 -- xt ) p{ call{} call{} }p ;
: partial1 ( x xt -- xt1 ) swap k co ;
== *Simplification* of curry3
: curry3
p{ p{
p{ lit{} lit{ lit{} }lit call{ lit{ lit{} }lit }call }p
\ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
\ this fragment is almost curry2 core in deeper nesting
}p }p
;
⇔ \ (2.1) factor it out into separate anonymous function
: curry3
p{
p{
p{ lit{ lit{} }lit call{ lit{ lit{} }lit }call }p
swap
p{ lit{} call{} }p
}p
}p
;
⇔ \ (2.2) lift it up
: curry3
p{
p{ lit{} call{ lit{} }call }p
p{
lit{}
swap
p{ lit{} call{} }p
}p
}p
;
⇔ \ (2.3) factor it out into separate anonymous function again,
\ refactor the second part (see also (1.1) )
: curry3
p{
p{ lit{} call{ lit{} }call }p
}p
p{
call{}
p{
p{ lit{} call{ lit{} }call }p
}p
}p
;
⇔ \ (2.4) substitute curry2 in place of its body
: curry3
curry2
p{
call{}
curry2
}p
;
⇔ \ (2.5) lift up the second curry2
: curry3
curry2 'curry2
swap p{ call{} call{} }p
;
⇔ \ (2.6) use rco combinator
: curry3 curry2 'curry2 rco ;
And we have got a simple solution for puzzle 2.
Puzzle 3 is solved via generalization of definitions for curry3, curry4,
curry5.
=== *The solution in bare ANS Forth*
: k ( x -- xt ) >r :noname r> postpone literal postpone ; ;
: co ( xt2 xt1 -- xt ) 2>r :noname 2r> compile, compile, postpone ; ;
: partial1 ( 1*x xt -- xt1 ) swap k co ;
: curry2 ( xt -- xt1 ) ['] partial1 partial1 ;
: curry3 ( xt -- xt1 ) curry2 ['] curry2 swap co ;
0 VALUE xt-of-curry
: curry ( xt u -- xt1 )
1- ?dup 0= if exit then
xt-of-curry partial1 swap curry2 co
; ' curry TO xt-of-curry
\ 10 lines. With 'itself' word it could be 6 lines!
\ testcase
: execute-balance ( i*x xt -- j*x n ) depth 1- >r execute depth r> - ;
: execute-balanced ( i*x xt n -- j*x ) \ j = i + n
>r execute-balance r> = if exit then -11 throw \ result out of range
;
: a ( xt x -- x1 ) swap 0 execute-balanced ;
:noname cr .s cr - ; curry2 3 a 4 a . cr \ 1
:noname cr .s cr - * ; curry3 3 a 4 a 5 a . cr \ 5
:noname cr .s cr - * ; 3 curry 3 a 4 a 5 a . cr \ 5
:noname cr .s cr - * + ; 4 curry 1 a 2 a 3 a 4 a . cr \ 7
:noname cr .s cr - + * - ; 5 curry 1 a 2 a 3 a 4 a 5 a . cr \ -11
--
Ruvim