1. init.e - standard library candidate

I came up with a small file that makes it easier to make code (especially GUI code) more modular. Think of a Win32Lib or wxEuphoria application, where you would create all of your widgets and assign them to constants. This gets really unwieldy for large apps. And when you start splitting code into multiple files, you have to be careful that they're included in the right order so that things get initialized properly.

So I came up with init.e, and I've found myself using it in lots of projects, and it seems like the sort of thing others might find useful:

namespace init 
 
include std/map.e 
 
map init_routines = map:new() 
 
export procedure register( sequence name, integer rid = routine_id("init") ) 
	map:put( init_routines, name, rid ) 
end procedure 
 
export procedure ensure( sequence name ) 
	integer rid = map:get( init_routines, name, -1 ) 
	if rid != -1 then 
		map:remove( init_routines, name  ) 
		call_proc( rid, {} ) 
	end if 
end procedure 
 
export procedure all() 
	sequence r = map:pairs( init_routines ) 
	for i = 1 to length(r) do 
		if map:has( init_routines, r[i][1] ) then 
			map:remove( init_routines, r[i][1] ) 
			call_proc( r[i][2], {} ) 
		end if 
	end for 
	map:clear( init_routines ) 
end procedure 
 

The idea is that you call init:register( "some name" ) (it defaults to a procedure named init(), but you can put whatever you want). And then inside of you init's, you can call init:ensure( "some dependency" )), which will cause that init routine to be called if it hasn't already.

And you just call init:all() at the bottom of your main program, or just before you call WinMain() or whatever.

Matt

new topic     » topic index » view message » categorize

2. Re: init.e - standard library candidate

mattlewis said...

I came up with a small file that makes it easier to make code (especially GUI code) more modular. Think of a Win32Lib or wxEuphoria application, where you would create all of your widgets and assign them to constants. This gets really unwieldy for large apps. And when you start splitting code into multiple files, you have to be careful that they're included in the right order so that things get initialized properly.

So I came up with init.e, and I've found myself using it in lots of projects, and it seems like the sort of thing others might find useful:

The idea is that you call init:register( "some name" ) (it defaults to a procedure named init(), but you can put whatever you want). And then inside of you init's, you can call init:ensure( "some dependency" )), which will cause that init routine to be called if it hasn't already.

And you just call init:all() at the bottom of your main program, or just before you call WinMain() or whatever.

Matt

Looks nice. But I am a dimwit. is there a possibility of seeing an example or two?
Thanks

new topic     » goto parent     » topic index » view message » categorize

3. Re: init.e - standard library candidate

EUWX said...
mattlewis said...

I came up with a small file that makes it easier to make code (especially GUI code) more modular. Think of a Win32Lib or wxEuphoria application, where you would create all of your widgets and assign them to constants. This gets really unwieldy for large apps. And when you start splitting code into multiple files, you have to be careful that they're included in the right order so that things get initialized properly.

So I came up with init.e, and I've found myself using it in lots of projects, and it seems like the sort of thing others might find useful:

The idea is that you call init:register( "some name" ) (it defaults to a procedure named init(), but you can put whatever you want). And then inside of you init's, you can call init:ensure( "some dependency" )), which will cause that init routine to be called if it hasn't already.

And you just call init:all() at the bottom of your main program, or just before you call WinMain() or whatever.

Matt

Looks nice. But I am a dimwit. is there a possibility of seeing an example or two?
Thanks

Sure. I'm currently refactoring my wxIDE (I also have done this with the Win32Lib IDE). Here are some exerpts:

-- wxide.exw 
 
include init.e 
include main.e 
include menu.e 
include settings.e 
 
namespace main 
 
include wxeu/wxeud.e 
 
... 
 
 
init:all()     -- calls all of the initialization routines 
wxMain( main ) -- show the main window and start interacting with the user 
 
-- main.e 
include wxeu/wxeud.e 
 
include init.e 
 
init:register( "main" ) 
 
export atom 
	main, 
	status, 
	win, 
        notebook 
 
procedure init() 
	init:ensure( "settings" ) 
	 
	main = create( wxFrame, {0, -1, "wxIDE", win_pos[1], win_pos[2], win_size[1], win_size[2], 
		wx_or_all( {wxDEFAULT_FRAME_STYLE, wxNO_FULL_REPAINT_ON_RESIZE}) }) 
	status = create( wxStatusBar, {main}) 
	win = create( wxPanel, {main, -1, -1, -1, -1, -1, wxCLIP_CHILDREN}) 
	notebook        = create( wxNotebook, {win, -1, -1, -1, -1, -1, wxCLIP_CHILDREN}) 
 
        ... 
end procedure 
 
-- menu.e 
include wxeu/wxeud.e as wx 
 
include init.e 
include main.e 
init:register( "menu" ) 
 
export atom 
	menu_bar, 
	file_menu, 
	file_open, 
	file_exit, 
        ... 
 
procedure init() 
	init:ensure( "main" ) 
	init:ensure( "settings" ) 
	menu_bar = create( wxMenuBar, {main}) 
	file_menu         = create( wxMenu, {menu_bar, "&File"}) 
	file_new          = create( wxMenuItem, {file_menu, wxID_NEW, "&New"}) 
	file_open         = create( wxMenuItem, {file_menu, wxID_OPEN,"&Open\tCtrl-O"}) 
	file_reopen       = create( wxMenuItem, {file_menu, wxID_REOPEN, "&Reopen File"}) 
	file_close        = create( wxMenuItem, {file_menu, close_id, "&Close\tCtrl-W"}) 
	file_save         = create( wxMenuItem, {file_menu, wxID_SAVE, "&Save"}) 
	file_save_as      = create( wxMenuItem, {file_menu, wxID_SAVEAS, "Save &As"}) 
	file_save_project = create( wxMenuItem, {file_menu, new_id(), "Save &Project"}) 
	append_separator( file_menu ) 
	file_exit = create( wxMenuItem, {file_menu, wxID_EXIT, "E&xit"}) 
 
end procedure 

The idea is to make it easy to take initialization out of the top level of execution, and especially to make it easy to keep dependencies straight. In this case, you can't create the menu until you've created the main window, etc.

Matt

new topic     » goto parent     » topic index » view message » categorize

4. Re: init.e - standard library candidate

mattlewis said...
EUWX said...

Looks nice. But I am a dimwit. is there a possibility of seeing an example or two?
Thanks

Sure. I'm currently refactoring my wxIDE (I also have done this with the Win32Lib IDE). Here are some exerpts:

......

The idea is to make it easy to take initialization out of the top level of execution, and especially to make it easy to keep dependencies straight. In this case, you can't create the menu until you've created the main window, etc.

Matt

Thanks for the prompt reply and demo. I will work on it. I had suspended my work with euphoria and wxeuphoria, as i got carried away with the Raspberry Pi. I will have to get back to it all.

new topic     » goto parent     » topic index » view message » categorize

5. Re: init.e - standard library candidate

mattlewis said...
export procedure register( sequence name, integer rid = routine_id("init") ) 
	map:put( init_routines, name, rid ) 
end procedure 

You've inadvertently solved a quandary that has been plaguing me for years: How do I pass only the name of a routine to an event handler, instead of embedding routine_id() in the function call? I had no idea that initializer values were called inline, which is what seems to make this magic work.

Quite simply...

public procedure SetHandler( atom this, atom id, atom event, sequence handler, integer rid = routine_id(handler) ) 
 
    if length(handler) > 0 and rid = -1 then 
        crash( "Routine %d not found!\n", {handler} ) 
    end if 
 
    -- store handler... 
 
end procedure 
 
-- later... 
 
SetHandler( frame, wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, "MyFrame:OnClick" ) 

Am I wrong, or should this work as I expect?

-Greg

new topic     » goto parent     » topic index » view message » categorize

6. Re: init.e - standard library candidate

ghaberek said...

You've inadvertently solved a quandary that has been plaguing me for years: How do I pass only the name of a routine to an event handler, instead of embedding routine_id() in the function call? I had no idea that initializer values were called inline, which is what seems to make this magic work.

Quite simply...

public procedure SetHandler( atom this, atom id, atom event, sequence handler, integer rid = routine_id(handler) ) 
 
    if length(handler) > 0 and rid = -1 then 
        crash( "Routine %d not found!\n", {handler} ) 
    end if 
 
    -- store handler... 
 
end procedure 
 
-- later... 
 
SetHandler( frame, wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, "MyFrame:OnClick" ) 

Am I wrong, or should this work as I expect?

Yes:

-- foo.ex 
include foo.e 
procedure bar() 
 
end procedure 
foo( "bar" ) 
 
-- foo.e 
 
export procedure foo( sequence name, integer rid = routine_id( name ) ) 
	? rid 
end procedure 

$ eui foo.ex 
0 

Success!

Matt

new topic     » goto parent     » topic index » view message » categorize

7. Re: init.e - standard library candidate

mattlewis said...
ghaberek said...

I had no idea that initializer values were called inline, which is what seems to make this magic work.

Yes:

-- foo.ex 
include foo.e 
procedure bar() 
 
end procedure 
foo( "bar" ) 
 
-- foo.e 
 
export procedure foo( sequence name, integer rid = routine_id( name ) ) 
	? rid 
end procedure 

$ eui foo.ex 
0 

Success!

Matt

I suspect this is simply broken. What should the following do?

somefile.e

constant A="1" 
 
global procedure putsomething(sequence s=A) 
    puts(1,s) 
end procedure 

someotherfile.exw

include somefile.e 
constant A="2" 
putsomething() 
if getc(0) then end if 

I'm quite happy that it prints 1, but that is not what was said, and routine_id is breaking scope by working...

Pete

new topic     » goto parent     » topic index » view message » categorize

8. Re: init.e - standard library candidate

petelomax said...

I suspect this is simply broken.

Based on what you demonstrate below, I can understand why you would think that. After making the following analysis, I find that I concur with your suspicion, but possibly for different reasons.

petelomax said...

What should the following do?

somefile.e

constant A="1" 
 
global procedure putsomething(sequence s=A) 
    puts(1,s) 
end procedure 

someotherfile.exw

include somefile.e 
constant A="2" 
putsomething() 
if getc(0) then end if 

It should print 1, as the resolution of variable names for defaulted parameters - when the default is used - should be done in the context of the file where the routine is declared, not in the context of where it is used.

petelomax said...

I'm quite happy that it prints 1, but that is not what was said, and routine_id is breaking scope by working...

Pete

Debatable.

Here's a modified version:

somefile.e

constant A="1" 
 
function it(sequence s) 
	return s*1 
end function 
 
global procedure putsomething(sequence s=A, sequence b=it(s), sequence c=repeat(s,10)) 
    puts(1,s) 
    puts(1,b) 
    ? c 
end procedure 

otherfile.exu

include somefile.e 
constant A="2" 
putsomething() 
putsomething(A) 

It first prints out "11" followed by a sequence containing sequences of 49 (or "1"), but the second time around it prints out "22" etc. When a preceding defaulted parameter is overridden, and succeeding defaulted parameters which use it are not, then they should use the new caller-passed overridden value in preference to the original, defaulted value.

Thus, like repeat() and it() in the example above, a defaulted parameter that uses routine_id(s) in the same way should expect that when s is overriden, then the following parameter(s) that use routine_id(s) will use the new overidden value for s as well.

However, routine_id() is unique in that, like repeat() or it(), routine_id() relies on scoping to interpret the value of its parameter. So, what should routine_id() do? Should it use the scope where the routine is called, or the scope where the routine is defined? This only applies to routine_id(), since no other routine cares about scope in the same way when it is called.

I contend that the most natural way for this to work is that routine_id() should use the scope where the routine with the defaulted parameter is defined when no relevant variables are overidden by the caller (as the defaulted routine_id() call probably points to a local routine that may not be visible in the caller's scope), but when overriding occurs then it should use the caller's scope (as the caller probably wants to take the routine id of a local routine that was immediately defined - e.g. setHandler() and routine_id() are typically called together immediately after the declaration of the handler routine).

Based on that, I would conclude that this is indeed broken, because routine_id() always defaults to the callers scope.

Example:

somefile.e

constant A="1" 
constant AA="it" 
 
function it(sequence s) 
	return s*1 
end function 
 
global procedure putsomething(sequence s=A, sequence b=it(s), sequence c=repeat(s,10), sequence d=AA, integer e=routine_id(d)) 
    puts(1,s) 
    puts(1,b) 
    ? c 
    ? e 
end procedure 

otherfile.exu

include somefile.e 
constant A="2" 
procedure AA() 
end procedure 
putsomething(A) 

This prints out -1 at the end, when it should print out a valid routine_id() for the routine "it".

That said, I suspect that fixing routine_id() to work intelligently in what I contend is the most natural manner is probably very difficult, while working around this behavior is really easy.

If the current behavior is kept as is, then the caller can simply manually invoke routine_id() if the integer holding the routine id is -1 and the sequence holding the routine name is the same as the default.

Example:

constant A="1" 
constant AA="it" 
 
function it(sequence s) 
	return s*1 
end function 
 
global procedure putsomething(sequence s=A, sequence b=it(s), sequence c=repeat(s,10), sequence d=AA, integer e=routine_id(d)) 
    puts(1,s) 
    puts(1,b) 
    ? c 
    if equal(d, AA) and e = -1 then 
    	e = routine_id(AA) 
    end if 
    ? e 
end procedure 

otherfile.exu

include somefile.e 
constant A="2" 
procedure AA() 
end procedure 
putsomething(A) 

However, altering routine_id() so it always uses the scope of the callee is not useful, as the callee has no way to compenstate and discover the real routine id that is meant to be called from just the string. Instead, the integer with the routine_id() call must be overriden everytime - most likely by another handwritten routine_id() call directly in the caller (as is typically done with setHandler() ). This makes embedding a routine_id() call inside of a defaulted parameter unconditionally useless.

So, yes, maybe it is technically broken, but if it can't be fixed, then breaking it in this way is better than the alternative.

new topic     » goto parent     » topic index » view message » categorize

9. Re: init.e - standard library candidate

jimcbrown said...
petelomax said...

I suspect this is simply broken.

I find that I concur with your suspicion, but possibly for different reasons. So, what should routine_id() do? Should it use the scope where the routine is called, or the scope where the routine is defined? This only applies to routine_id(), since no other routine cares about scope in the same way when it is called.
...
Based on that, I would conclude that this is indeed broken, because routine_id() always defaults to the callers scope.
...
That said, I suspect that fixing routine_id() to work intelligently in what I contend is the most natural manner is probably very difficult, while working around this behavior is really easy.
...
However, altering routine_id() so it always uses the scope of the callee is not useful
This makes embedding a routine_id() call inside of a defaulted parameter unconditionally useless.

So, yes, maybe it is technically broken, but if it can't be fixed, then breaking it in this way is better than the alternative.

Thanks for the excellent and detailed reply.
I have figured it out now; the default expression is effectively parsed in the called context (that part I really misread), but evaluated in the callee context, which as you point out makes no difference except for routine_id().

FWIW, I plan (not particularly soon) in Phix to change routine_id to:

global function routine_id(string name, boolean use_callee_context=false) 
... 
procedure x(integer rid=routine_id("something",true)) 

which would allow what I see as the required extra flexibility. The extra parameter would not be valid in top-level code.

Regards, Pete

new topic     » goto parent     » topic index » view message » categorize

10. Re: init.e - standard library candidate

jimcbrown said...

However, altering routine_id() so it always uses the scope of the callee is not useful, as the callee has no way to compenstate and discover the real routine id that is meant to be called from just the string. Instead, the integer with the routine_id() call must be overriden everytime - most likely by another handwritten routine_id() call directly in the caller (as is typically done with setHandler() ). This makes embedding a routine_id() call inside of a defaulted parameter unconditionally useless.

So, yes, maybe it is technically broken, but if it can't be fixed, then breaking it in this way is better than the alternative.

I actually didn't think this would work when I first tried it, because I recall going to a bit of effort to make variables evaluate in the routine's scope. This is especially important if the routine refers to something local that isn't visible outside of the file.

Routine id calls are somewhat special in that they transmit their calling point. I think we could make them be evaluated in a similar fashion to everything else, but I'm not certain that we'd really want to, since this is a really handy thing. And it's easy enough to get a local routine id by simply calculating it at the top level as a constant and using that as the default value.

Matt

new topic     » goto parent     » topic index » view message » categorize

11. Re: init.e - standard library candidate

mattlewis said...
jimcbrown said...

However, altering routine_id() so it always uses the scope of the callee is not useful, as the callee has no way to compenstate and discover the real routine id that is meant to be called from just the string. Instead, the integer with the routine_id() call must be overriden everytime - most likely by another handwritten routine_id() call directly in the caller (as is typically done with setHandler() ). This makes embedding a routine_id() call inside of a defaulted parameter unconditionally useless.

So, yes, maybe it is technically broken, but if it can't be fixed, then breaking it in this way is better than the alternative.

I actually didn't think this would work when I first tried it, because I recall going to a bit of effort to make variables evaluate in the routine's scope. This is especially important if the routine refers to something local that isn't visible outside of the file.

I seem to recall that was actually a ticket for that.

mattlewis said...

Routine id calls are somewhat special in that they transmit their calling point. I think we could make them be evaluated in a similar fashion to everything else, but I'm not certain that we'd really want to, since this is a really handy thing. And it's easy enough to get a local routine id by simply calculating it at the top level as a constant and using that as the default value.

Matt

I said this already - in the part of my post that you quoted. You worded it better, though. tongue

new topic     » goto parent     » topic index » view message » categorize

12. Re: init.e - standard library candidate

jimcbrown said...
mattlewis said...

I actually didn't think this would work when I first tried it, because I recall going to a bit of effort to make variables evaluate in the routine's scope. This is especially important if the routine refers to something local that isn't visible outside of the file.

I seem to recall that was actually a ticket for that.

ticket:563

Matt

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu