Hi all, I want to popup this topic again. And I will start with some context: recently, I had debugged pcmanfm file manager's segmentation faults, that happen during navigation through hidden folders. I found out, that the cause of this error is in per-directory settings of displaying hidden files, which means, that if you will enable showing of hidden files in $HOME directory, it will be stored only for this directory. So $HOME/another_dir will not display hidden files, until you will enable this option for directory explicitly.
Here is sequence diagram of calls, that leads to segfaults (you can copy-paste it to stackedit.io, which will render it): ```mermaid sequenceDiagram participant user participant side_panel participant main_window user->>side_panel:1 click on hidden directory gtk->>side_panel:2 on_sel_change() side_panel->>main_window:3 cancel_pending_chdir() side_panel->>gtk:4 emit_chdir_if_needed() side_panel->>gtk:5 gtk_tree_selection_get_selected() gtk->>side_panel:6 item iterator side_panel->>gtk:7 g_signal_emit("CHDIR") gtk->>tab_page:8 "CHDIR" signal tab_page->>tab_page:9 fm_tab_page_chdir_without_history() tab_page->>app_config:10 fm_app_config_get_config_for_path() app_config->>tab_page:11 show_hidden=0 tab_page->>tab_page:12 page->show_hidden=0 tab_page->>fm_folder:13 fm_folder_view_set_show_hidden(0) fm_folder->>fm_folder:14 fm_folder_model_set_show_hidden(0) fm_folder->>fm_folder:15 fm_folder_apply_filters() fm_folder->>gtk:16 g_signal_emit("row-deleting") fm_folder->>gtk:17 g_signal_emit("filter-changed") gtk->>fm_folder:18 signal "filter-changed" fm_folder->>gtk:19 g_signal_emit("filter-changed") gtk->>main_window:20 signal "filter-changed" main_window->>main_window:21 on_folder_view_filter_changed() main_window->>app_config:22 fm_app_config_save_config_for_path(show_hidden=0) tab_page->>side_panel:23 fm_side_pane_set_show_hidden(0) side_panel->>side_panel:24 fm_dir_tree_view_set_property(0) side_panel->>side_panel:25 fm_dir_tree_model_set_show_hidden(0) side_panel->>side_panel:26 item_hide_hidden_children() side_panel->>tab_page:27 show_hidden=0 side_panel->>gtk:28 g_signal_emit("CHDIR") gtk->>main_window:29 signal "CHDIR" main_window->>main_window:30 on_side_pane_chdir() main_window->>main_window:31 fm_tab_page_chdir() side_panel->>side_panel:32 gtk_tree_model_get_path(item iterator) side_panel->>side_panel:33 fm_dir_tree_model_load_row(item iterator) ``` In this diagram, at step 6 folders gets **iterator**, which will be used at steps 32 and 33, but this iterator is already invalid, because at step 26 hidden directories will be removed from side panel. This may cause a segfault. So, I have started to think about how should look gtk2 API(in non-C language, for GtkTreeIter, as a small example) to prevent such issues, given those requirements: 1. usage of ffi calls to gtk2 library(nobody wants to reimplement a wheel, right?); 2. When GtkTreeIter is in the scope, it should be impossible to add/delete content to/from GtkTreeView; 3. gtk is not thread-safe library, so don't bother with it in API for now. Initially, I thought that linear types may be helpful here, but then, I had realized, that g_signal_emit() is ffi call, which calls another signal handler, which and I doubt, that linear types will be able to track such behavior. Here is, obviously incorrect snippet(due to my lack of knowledge of ATS2): extern fn g_signal_emit(string): void = "ext#" extern fn g_signal_connect(name: string, handler: ptr->void, ptr): void = "ext#" fn handler(tree: ptr): void = let val () = gtk_tree_view_delete_by_index(tree, 0) (* for simplicity, let's consider, that this is how it is being deleted *) implement main0(argc,argv) = let val (tvpf1 | tv) = gtk_tree_view_new_with_model () (* insert 1 row into tree view *) val () = g_signal_connect( "CHDIR", handler, tv) val (p | iter) = gtk_tree_selection_get_selected( tvpf1 | tv) (* get iterator on a row 0 *) val () = g_signal_emit( "CHDIR") (* handler will delete row 0 *) val (p | iter) = gtk_tree_model_get_path( p | iter) (* iter is invalid here, can ats catch this? *) in end So I came to conclusion, that (with my skills), only Haskell/Idris/Agda(/Coq?) way of referential transparency is the only way to prevent such errors by allowing side effects only inside `IO` "actions". Example solution in Haskell (not compilable, just to get the idea): Iterator.hs: module Iterator ( IteratorM -- constructor is not being exported - IteratorM values can only be created inside this module , Iterator , gtk_tree_selection_with_selected , gtk_tree_model_get_path ) where import Foreign.Ptr import GtkTreeView newtype Iterator = Iterator (Ptr ()) newtype IteratorM a = IteratorM { runIterator :: IO a } -- just instance Monad IteratorM where (>>=) (IteratorM left) second = IteratorM $ left >>= \arg-> case second arg of IteratorM next-> next return v = IteratorM (return v) instance Applicative IteratorM where pure v = IteratorM (pure v) -- :: f (a -> b) -> f a -> f b (<*>) (IteratorM first) (IteratorM second) = IteratorM ( first <*> second) instance Functor IteratorM where -- :: (a -> b) -> f a -> f b fmap f (IteratorM first) = IteratorM (fmap f first) foreign import ccall "gtk_tree_selection_get_selected" :: Ptr () -> IO (Ptr ()) gtk_tree_selection_with_selected :: GtkTreeView-> (Iterator-> IteratorM a)-> IO a gtk_tree_selection_with_selected (GtkTreeView tree) nested = c_gtk_tree_selection_get_selected tree >> \ptr-> nested (Iterator ptr) gtk_tree_view_iter_set_color :: Iterator-> Color-> IteratorM () gtk_tree_view_iter_set_color (Iterator iter) color = IteratorM (c_gtk_tree_view_iter_set_color iter color) foreign import ccall "gtk_tree_model_get_path" :: Ptr ()-> Ptr ()-> IO (Ptr ()) gtk_tree_model_get_path :: GtkTreeView-> Iterator-> IteratorM GtkTreePath -- let's pretend, that it uses GtkTreeView for simplicity gtk_tree_model_get_path (GtkTreeView tree) (Iterator iter) = IteratorM (c_gtk_tree_model_get_path tree iter) Main.hs module Main where import Iterator import GtkTreeView foreign import ccall "g_signal_emit" g_signal_emit:: Ptr CChar -> IO () foreign import ccall "g_signal_connect" g_signal_connect :: Ptr CChar-> (Ptr ()-> IO ())-> Ptr () -> IO () handler :: GtkTreeView-> IO () handler tree = gtk_tree_view_delete_by_index tree 0 -- deleting first row main :: IO () main = do tree <- gtk_tree_view_new_with_model g_signal_connect "CHDIR" handler tree -- insert 1 row into tree here ... gtk_tree_selection_with_selected tree $ \iter-> do let a = 1 + 2 -- valid, can use pure expressions inside IteratorM path <- gtk_tree_model_get_path iter -- valid gtk_tree_view_set_color iter Blue -- valid {- g_signal_emit "CHDIR" -- compile time error here, as it not IteratorM -} g_signal_emit "CHDIR" -- valid here, as we are not anymore inside gtk_tree_slection_with_selected nested: iter is out of scope now One possible issue here is that Iterator value can escape from `gtk_tree_selection_with_selected` function, but this can be fixed with return type of `IO ()`. So this example motivates me to search for ability to have similar modeling of side effects in ATS2 by using something like `datatype IO (a:t@ype) = IO of (RealWorld-> (RealWorld, a)`. Or am I missing something and this issue can be solved in ATS2 as well, without rewriting standard library and patching compiler? -- You received this message because you are subscribed to the Google Groups "ats-lang-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to ats-lang-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/ats-lang-users/3a2e5526-0dc7-41f0-b992-340b0e6a5564%40googlegroups.com.