Question to all you Gophers out there: How do you deal with custom errors that include more information and different kinds of matching them?
I started with a simple var ErrPermissionNotAllowed = errors.New("permission not allowed")
. In my function I then wrap that using fmt.Errorf("%w: %v", ErrPermissionNotAllowed, failedPermissions)
. I can match this error using errors.Is(err, ErrPermissionNotAllowed)
. So far so good.
Now for display purposes Iād also like to access the individual permissions that could not be assigned. Parsing the error message is obviously not an option. So I thought, I create a custom error type, e.g. type PermissionNotAllowedError []Permission
and give it some func (e PermissionNotAllowedError) Error() string { return fmt.Sprintf("permission not allowed: %v", e) }
. My function would then return this error instead: PermissionNotAllowedError{failedPermissions}
At some layers I donāt care about the exact permissions that failed, but at others I do, at least when accessing them. A custom func (e PermissionNotAllowedError) Is(target err) bool
could match both the general ErrPermissionNotAllowed
as well as the PermissionNotAllowedError
. Same with As(ā¦)
. For testing purposes the PermissionNotAllowedError
would then also try to match the included permissions, so assertions in tests would work nicely. But having two different errors for different matching seems not very elegant at all.
Did you ever encounter this scenario before? How did you address this? Is my thinking flawed?
I played around with parsers. This time I experimented with parser combinators for twt message text tokenization. Basically, extract mentions, subjects, URLs, media and regular text. Itās kinda nice, although my solution is not completely elegant, I have to say. Especially my communication protocol between different steps for intermediate results is really ugly. Not sure about performance, I reckon a hand-written state machine parser would be quite a bit faster. I need to write a second parser and then benchmark them.
lexer.go and newparser.go resemble the parser combinators: https://git.isobeef.org/lyse/tt2/-/commit/4d481acad0213771fe5804917576388f51c340c0 Itās far from finished yet.
The first attempt in parser.go doesnāt work as my backtracking is not accounted for, I noticed only later, that I have to do that. With twt message texts there is no real error in parsing. Just regular text as a āfallbackā. So it works a bit differently than parsing a real language. No error reporting required, except maybe for debugging. My goal was to port my Python code as closely as possible. But then the runes in the string gave me a bit of a headache, so I thought I just build myself a nice reader abstraction. When I noticed the missing backtracking, I then decided to give parser combinators a try instead of improving on my look ahead reader. It only later occurred to me, that I could have just used a rune slice instead of a string. With that, porting the Python code should have been straightforward.
Yeah, all this doesnāt probably make sense, unless you look at the code. And even then, you have to learn the ropes a bit. Sorry for the noise. :-)
Any good ideas on how to maintain ~/go/pkg/mod and to remove old garbage?
I started reading the proposal to introduce operator overloading in Go version 2 that I like to see: https://github.com/golang/go/issues/27605 Now a few hours later I ended up at this gem. Write a program that makes 2+2=5: https://codegolf.stackexchange.com/questions/28786/write-a-program-that-makes-2-2-5 There are some awesone solutions. :-)
$name$
and then dispatch the hashing or checking to its specific format.
@xuu@txt.sour.is Really sweet! Why did you pick MD5 as the example?
@prologic@twtxt.net Alright, thereās some erroneous markdown parsing going on, I reckon. In my original twt I have a code block surrounded by three backticks. The code block itself contains a single backtick. However, at least for rendering, yarnd shows three backticks instead (not sure if my markdown is invalid, though):
<author>
from <entry>
s to <feed>
, Newsboat marked all old affected articles as unread. IDs were untouched, of course. Need to investigate that. Had something similar happen with another feed change I did some time ago. Can't remember what that was, though.
Great, last system update broke something, building from current master I get:
/usr/bin/ld: /lib/x86_64-linux-gnu/libm.so.6: unknown type [0x13] section `.relr.dyn'
What the heck!?
And it also appears that Iām not really able to reproduce this unread bug. It only kind of works a single time. And it has something to do with my config. Not sure what it is yet. I also noticed that the <updated>
timestamps in the entries somehow shifted between the old and new feed. Da fuq!?
Hmmm, after fixing my feeds to move the <author>
from <entry>
s to <feed>
, Newsboat marked all old affected articles as unread. IDs were untouched, of course. Need to investigate that. Had something similar happen with another feed change I did some time ago. Canāt remember what that was, though.
@mckinley@twtxt.net Thank you! I didnāt even know about signing and encrypting XML documents. Right, RSS is a little bit messy.
Unfortunately, the autodiscovery document in one of your linked resources does not exist anymore. What annoys me in Atom is the distinction between <id>
and <link>
. I always want my URL also to be my ID, so I have to duplicate that ā unnecessarily in my opinion.
Also, never found a good explanation why I should add <link rel="self" ⦠/>
to my feeds. I just do, but I donāt understand why. The W3C Feed Validation Service says:
[ā¦] This value is important in a number of subscription scenarios where often times the feed aggregator only has access to the content of the feed and not the location from which the feed was fetched.
This just sounds like a very questionable bandaid to bad software architecture. Why would the feed parser need access to the feed URL at this stage? And if so, why not just pass down the input source? Just doesnāt make sense to me.
Also, I just noticed that I reference the http://purl.org/rss/1.0/modules/syndication/
namespace, but donāt use it in most of my feeds. Gotta fix that. Must have copied that from my yfav feed without paying attention what Iām doing.
Your article made me reread the Atom spec and I found out, that I can omit the <author>
in the <entry>
when I specify a global <author>
at <feed>
level. Awesome! Will do that as well and thus reduce the feed size.
We had a nice sunset a few minutes ago:
Welcome back, @quark@ferengi.one! Your web server doesnāt send back a Last-Modified
header for your feed, so the official twtxt client complains not to cache it. I just fixed that, so that tt shows your feed (of course no progress has been made in the meantime). And the Date
header of your server seems to be quite funny, too. ;-)
@mckinley@twtxt.net Haha, while composing I was wondering two or three times whether I should throw my thoughts in an HTML page instead. But out of utter laziness I discarded that idea. ĀÆ_(ć)_/ĀÆ
@prologic@twtxt.net Error handling especially in Go is very tricky I think. Even though the idea is simple, itās fairly hard to actually implement and use in a meaningful way in my opinion. All this error wrapping or the lack of it and checking whether some specific error occurred is a mess. errors.As(ā¦)
just doesnāt feel natural. errors.Is(ā¦)
only just. I mainly avoided it. Yesterday evening I actually researched a bit about that and found this article on errors with Go 1.13. It shed a little bit of light, but I still have a long way to go, I reckon.
We tried several things but havenāt found the holy grail. Currently, we have a mix of different styles, but nothing feels really right. And having plenty of different approaches also doesnāt help, thatās right. I agree, error messages often end up getting wrapped way too much with useless information. We havenāt found a solution yet. We just noticed that it kind of depends on the exact circumstances, sometimes the caller should add more information, sometimes itās better if the callee already includes what it was supposed to do.
To experiment and get a feel for yesterdayās research results I tried myself on the combined log parser and how to signal three different errors. Iām not happy with it. Any feedback is highly appreciated. The idea is to let the caller check (not implemented yet) whether a specific error occurred. That means I have to define some dedicated errors upfront (ErrInvalidFormat
, ErrInvalidStatusCode
, ErrInvalidSentBytes
) that can be used in the err == ErrInvalidFormat
or probably more correct errors.Is(err, ErrInvalidFormat)
check at the caller.
All three errors define separate error categories and are created using errors.New(ā¦)
. But for the invalid status code and invalid sent bytes cases I want to include more detail, the actual invalid number that is. Since these errors are already predefined, I cannot add this dynamic information to them. So I would need to wrap them Ć la fmt.Errorf("invalid sent bytes '%s': %w", sentBytes, ErrInvalidSentBytes")
. Yet, the ErrInvalidSentBytes
is wrapped and can be asserted later on using errors.Is(err, ErrInvalidSentBytes)
, but the big problem is that the message is repeated. I donāt want that!
Having a Python and Java background, exception hierarchies are a well understood concept Iām trying to use here. While typing this long message it occurs to me that this is probably the issue here. Anyways, I thought, I just create a ParseError
type, that can hold a custom message and some causing error (one of the three ErrInvalid*
above). The custom message is then returned at Error()
and the wrapped cause will be matched in Is(ā¦)
. I then just return a ParseError{fmt.Sprintf("invalid sent bytes '%s'", sentBytes), ErrInvalidSentBytes}
, but that looks super weird.
I probably need to scrap the āparent errorā ParseError
and make all three āsuberrorsā three dedicated error types implementing Error() string
methods where I create a useful error messages. Then the caller probably could just errors.Is(err, InvalidSentBytesError{})
. But creating an instance of the InvalidSentBytesError
type only to check for such an error category just does feel wrong to me. However, it might be the way to do this. I donāt know. To be tried. Opinions, anyone? Implementing a whole new type is some effort, that I want to avoid.
Alternatively just one ParseError
containing an error kind enumeration for InvalidFormat
and friends could be used. Also seen that pattern before. But that would then require the much more verbose var parseError ParseError; if errors.As(err, &parseError) && parseError.Kind == InvalidSentBytes { ⦠}
or something like that. Far from elegant in my eyes.
Iām trying to switch from Konversation to irssi. Letās see how that goes. Any irssiers out there who can recommend specific settings or scripts? I already got myself trackbar.pl
and nickcolor.pl
as super-essentials. Also trying window_switcher.pl
. Somehow my custom binds for Ctrl+1/2/3/etc.
to switch to window 1/2/3/etc. doesnāt do anything: { key = "^1"; id = "change_window"; data = "1"; }
(I cannot use the default with Alt
as this is handled by my window manager). Currently, Iām just cycling with Ctrl+N/P
. Other things to solve in the near future:
- better, more colorful and compact theme (just removed clock from statusbar so far)
- getting bell/urgency hints working on arriving messages
- nicer tabs in status bar, maybe even just channel names and no indexes
- decluster status bar with user and channel modes (I never cared about those in the last decade)
@movq@www.uninformativ.de I usually only use eggs for baking or fry them for potatoes and spinach. @prologic@twtxt.net Why donāt you have them anymore? Did the fox get them all when the door didnāt close in time? ]:->
Alright, check this out. I just kinda completed todayās project of converting a jeans into a saw bag. Itās not fully done, the side seams on the flap need some more hand sewing, thatās for sure. No, I donāt have a sewing machine. Yet?
At first I wanted to put in the saw on the short side, but that would have made for more sewing work and increased material consumption. As a Swabian my genes force me to be very thrifty. Slipping in on the long side had the benefit of using the bottom trouser leg without any modification at all. The leg tapers slightly and gets wider and wider the more up you go. At the bottom itās not as extreme as at the top.
The bag is made of two layers of cloth for extra durability. The double layers help to hide the inner two metal snap fastener counter parts, so the saw blade doesnāt get scratched. Not a big concern, but why not doing it, literally no added efforts were needed. Also I reckon it cuts off the metal on metal clinking sounds.
The only downside I noticed right after I pressed in the receiving ends of the snap fasteners is that the flap overhangs the bag by quite a lot. I fear thatās not really user-friendly. Oh well. Maybe I will fold it shorter and sew it on. Letās see. The main purpose is to keep the folding saw closed, it only locks in two open positions.
Two buttons would have done the trick, with three I went a bit overkill. In fact the one in the middle is nearly sufficient. Not quite, but very close. But overkill is a bit my motto. The sides making up the bag are sewed together with like five stitch rows. As said in the introduction, the flap on the hand needs some more love.
Oh, and if I had made it in a vertical orientation I would have had the bonus of adding a belt loop and carrying it right along me. In the horizontal layout thatās not possible at all. The jeans cloth is too flimsy, the saw will immediately fall out if I open the middle button. Itās not ridgid enough. Anyways, I call it a success in my books so far. Definitely had some fun.
My first attempt of guessing a German word was a close failure: