Open hyperrealgopher opened 4 years ago
A lot of stuff could make better use of higher order functions. Something which comes to mind are stuff like progress module and the stuff like searchGet and gopherGet, etc.
The history items in progress.hs should go in their own module. They're currently in there because of a circular import problem.
Some of @Garmelon's suggestions:
[x] Explicit exports.
module Bla (foo, bar) where
. This way:
[x] Switch to using Data.Text
instead of String
If only to learn about how it works. Data.Text
usually has better performance than String
's linked-list-of-characters approach. It also has a few common string manipulation functions that String
is kinda missing. Some people argue for Text
to replace String
entirely.
[x] Rename GopherClient
to Gopher
Use the module only to model the gopher protocol side of things. Leave out functions that don't use that protocol representation (such as gopherGet
; NOTE: I'm guessing I should have a Network.hs
). As a rule of thumb: No IO required. Also make sure there are no partial functions (different than partially applied) and you don't use error or undefined anywhere (don't use them in other modules either, if at all possible).
Programming is pretty much all about manipulating data. Having a good representation for the data helps a lot. I'd say this becomes even more obvious in Haskell, since here you have better ways to represent your data out-of-the-box than in languages like Java or Python. When in doubt, roll your own types instead of using Either
(Either
already treats its left and right arguments differently due to its various instances. Left
is usually used to hold some sort of error, while Right
is used to hold a successful result).
[ ] Separate the UI into different widgets that only need to know their own state. This is where I'd need to know more about the UI and its different interactions: Try to separate the UI into different widgets that only need to know their own state. They should not need to know any sort of global state (GBS) nor rely on a common resource name type. You can use the way brick widgets are structured and structure your own widgets after those.
[ ] Design an application state and application using the widgets from the previous step. This application module should use the widgets, not the other way around. Try to keep features general and loosely coupled. Extract things into modules wherever it makes sense. A single client feature (like the history) does not necessarily need to be confined to a single module. For example, I could imagine one module that lets you store a history of states of any type, and a history widget that can display some of the data types from the history module.
Maybe even start UI/Modes/*.hs?
you're supposed to keep widgets in app state and manipulate things from there! that's much more efficient than rebuilding every dang time