I created https://chronograph.io back in 2014 so people can share accurate, live stopwatches and timers. Other sites "share" by putting the start epoch & duration in a url querystring; Chronograph is one of the first to actually share a session with concurrent edits.
Of the years, it's found a variety of use cases:
- Online scrum meetings.
- Recording starts & stops of races when they're geographically separated.
- Coordination of breaks between language interpreters.
- Syncing of podcast recording with multiple guests.
- Video game twitchers to broadcast when they'll play
Tiny amount of UI feedback; I automatically wanted to click “cloud-synchronized” at the top to find out how it works technically, since the text is blue and I’d expect it to be a link.
May be better to just italicize or bold that rather than change the color. Or better yet, actually make it a link to a short “how does it work” page
I'll do that, thanks. I've never had feedback that's indicated people wanted to know.
It uses NTP's clock synchronization algorithm to get the servers' time. This is done over a websocket as it's twice as fast as a typical ajax request/response. Then all changes are made using the browser's idea of server time.
At the bottom of the page, you can see your computer/phone's time offset and precision.
Heh. This is the second time I've posted this in less than 24 hours, but it's relevant. I won't even apologize for the plug as it's not something I particularly care about.
In case you want to go old school, I made a clock. It's a PWA so you can use it as a widget or app. It's open source - just click "View Source". (Actually, I may have ripped off most of the code, I can't remember, so don't do that.)
Why? I like analog clocks to keep track of time. To me they're like moving pie charts. So I made this for myself and will bring it up on a second screen or an HDTV if I need it. You'd be surprised how useful it can be.
That's a great service. I can think of various features to support more use cases:
* provide an optional label - for the use case where several clocks are used and shared.
* start and share several stop watches from a single page: start them one by one or together. For a race with multiple participants that either start sequentially or together.
* Support taking split times.
* Combine timer and stopwatch: count down from a set time, then start the stopwatch at zero. It currently does this already. This supports races like sailing where the actual start is preceded by a countdown period.
1) Click on the stopwatch timer title; you can edit it in place. Indeed, discovery of this is not great. My UX designs tends to be quite minimal. I'll try to make this more obvious.
2) I'm considering building use-case specific sites with the same backend to more adequately support each. I despise how many generic sites overload UI with junk aimed at certain power users for a use-case I don't have.
3) Splits are already supported for stopwatches.
4) This touches an occasionally requested feature of making sequential "entities" - most often in the context of circuit training. 5 minutes of bench pressing followed by 5 minutes of jumping jacks, etc. Allowing the addition of a stopwatch at the end of the sequence makes sense. Thanks
Former Google engineer here. My team used the open source Go B-Tree implementation but I replaced it with a non-interface version over a year ago because of performance issues. The lack of generics at the time wasn't a problem because code generation is quite easy via Google's build system.
Replacing interfaces is only part of the performance improvement. When it comes to the B-Tree, you want to improve data locality as much as possible. My implementation also replaced slices with arrays (again using code gen to specify the tree degree). This means each node on the heap literally contained all the keys and, and in some cases, the values themselves. It also helped to split the key/values into separate arrays so all the values didn't interlace the keys.
The insert/retrieval benchmark times compared to the open source version were significantly faster. Since this version relied on codegen, I never bothered to push it publicly. Though, there are tons of B-tree implementations on github, each touting themselves as fastest.
Go supporters see C++ templates as abomination, yet these performance problems can be easily solved by C++ templates. C++ templates can have integer value in its template parameter, so if we implement that B-Tree in C++, we don't have to resort to codegen at all, as the language is powerful enough.
Many years ago I created a site that similarly syncs time between server and clients for the purpose of sharing stopwatches & timers. I found that using a websocket connection yielded a higher precision.
That's really neat, thanks (do you have any benchmarks comparing it to Go's JSON?), but doesn't cover my use case. I want all the values in a map-like API. I don't know anything about the shape of the contents, names of the keys, etc.
Looked at both. ffjson is a library that generates the marshaling/unmarshaling Go code for your structs, so it doesn't support my use case at all. megajson is abandonware ("This tool is unmaintained. Please use ffjson instead").
go-codec [1] is very good, and can do things like interning string keys to reduce allocation overhead, but it's not faster than encoding/json when working on plain maps.
Indeed, it's frustrating when you're reading flat files of code. When reading foreign F#, I pull the code into Visual Studio so I can hover over the variables. It'd be quite convenient for GitHub to provide the type information on hover...
The package author means the error will be nil when the secret is 32 characters long; he doesn't mean you can completely ignore checking it.
Regarding when to use panic:
"The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values." http://blog.golang.org/defer-panic-and-recover
Panics should only be used in cases of programmer error or when the process is in an unrecoverable state.
> The package author means the error will be nil when the secret is 32 characters long; he doesn't mean you can completely ignore checking it.
But that's exactly what will happen. This comment will lead to ws, _ := gowork.NewServer("32 character secret"). Which is a worse situation than a panic(). I understand the normal rule for this, but "a foolish consistency..". In this case, the init value being 32 long can be compared to "the init value must be non-nil." A lot of non-error APIs will panic() if you pass nil, even in the stdlib.
Doing panic() here will lead to less room for error and confusion, in my opinion. Especially because the error is explicit and loud (panic()), it's worth considering breaking the rules for.
If a developer is frequently ignoring returned errors, then they have larger problems and it's their fault. Panic was not added to assist lazy programmers. Even though some API has solidified, an author could later add different return errors. Heck, if that function was calling other functions with returned errors, then the list of possible unique errors increases greatly. You should never ignore an err because you think you know the possible reasons.
The package author should modify the phrasing of that comment.
Thanks for the feedback (both of you!) - when writing this, I envisioned a use case where the secret was static within the application, so it is easy enough for the developer to check their secret is 32 characters and not have to bother with checking that particular error. I didn't use panic because although a non 32 character secret puts my library in an unrecoverable state, it doesn't mean the application implementing the library is also in an unrecoverable state, hence why it returns an error instead of panicing.
Also, in terms of the top comment in this chain, the work server (master) never gets work from the workers, the workers push completed work back to the server, maybe this isn't clear within the current docs. All communication between the worker and server has to be coordinated by the application implementing the library.
An idea is to include a function in the library called MustNewServer() that panics instead of returning an error. A usability perk of having single return is that the function can be chained, i. e.,
When a stranger is in my home at 3am while my spouse and two kids are upstairs sleeping, I won't risk judging what he/she will or will not do. Even if I were alone, I wouldn't take that chance. My intention is not to preserve "human life" but the lives of my family and myself.
> My intention is not to preserve "human life" but the lives of my family and myself.
The irony of course is that (statistically speaking) you would be vastly lowering you and your family's chances of surviving the event.
The chance that the intruder is there to murder you is ridiculously minuscule. It's so small that the people that do indiscriminately enter homes to murder get special nicknames like "Zodiac Killer" and Hollywood makes films about them.
Worrying about this type of intruder is irrational. Pulling a gun turns what is almost certainly a routine burglary into a life and death situation.
Of course. I just find the idea of guns making you safe so laughably absurd (and provably false). I'm sure they make you feel safer though. Maybe that's worth something.
Here's a good comedy skit about gun ownership (NSFW)
Guns don't make me feel safe. Being properly trained on how to use defensive force makes me feel safe.
The error in your logic is that you're imagining yourself with a weapon, which indeed, is laughable and provably unsafe.
Edit: not going to bother replying and further make this thread a gun debate. Just want to state that YouTube videos of comedic skits and accidents doesn't nullify any argument. Humans will make mistakes; that's a fact of life.
> Humans will make mistakes; that's a fact of life.
I agree. This is a fact of life. This combined with the utterly vanishingly tiny chance that you will be the victim of a serial killer make gun ownership for the purpose of safety absurd.
Own a gun all you want. But at least realize that you and your family are actually less safe because of it!
You're making incredible judgements about an individual who clearly spends the time to learn how to be a responsible gun owner. What you're equating is someone who says they know karate because they watch a lot of kung fu movies. This is a guy who regularly trains at a dojo.
Not everyone who owns a gun has the proper discipline to learn how to use their weapon. I call those people statistics, and they set a bad example for the rest of us who respect our tools.
I'm not passing judgement at all. I'm saying that having a device that is designed to suddenly and explosively discharge a projectile is inherently unsafe. To back up my claim I posted numerous videos, many of highly trained people, who have experienced accidental/negligent discharge.
Accidents happen. Even to the most highly trained and careful. The issue with gun accidents is that they have an incredibly high risk of being fatal. Enjoy guns all you like. Just stop pretending that you are safer around them or that you are immune to mistakes. No human is.
The use of `append` is absolutely clear. It always returns a new slice; the compiler forces you to take the value returned.
What scares you is the possibility that the underlying array may be copied to a newer, larger array giving enough space for the appended items. If there exists enough space, why bother with a new allocation? I want `append` to handle this for me. If you want different behavior, you can easily setup your own design with `copy`
Regarding this blog post, if you have multiple slices to the same array and are arbitrarily appending to any of those slices, then your design is wrong to begin with.
This isn't an edge-case but a lack of slice understanding.
You're describing how it actually works which is by way of 'shallow value' semantics which is precisely what the commenter is against. Such semantics requires understanding implementation without a simple opaque description. Either full mutability or 'deep value' semantics is simpler. For instance if the b: = a; followed by a[0] = 2 resulted on a copy on write, then b would be unaffected after the assignment of value. Of course the language and libraries do what they do and the user must be aware. Go was supposed to be easier, so copy-on-write semantics would have made more sense.
I'm not sure what you're trying to say. This is, IMO, nothing more than a lack of understanding. Is the result of `a := make([]struct{}, 0, 1); b := a; b = append(b, struct{}{}); println(len(a), len(b))` also surprising?
Your understanding is incorrect, too. append() only returns a new slice when "the capacity of s is not large enough to fit the additional values", in which case "append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array."
No, nicksardo's understanding was quite correct. Slices are values, and it is impossible to return the same one, because value semantics don't work like that.
The slice returned only points to a new array when the capacity is not large enough to fit additional values.
You seem to be confusing `array` with `slice`. AFAIK, append always returns a new slice. How could it not, when everything is copy/pass by value? A slice is essentially a struct with fields for len, cap and a pointer to an array.
Used to use PasswordBox but then switched to 1Password - I'm much happier. Unlike my experience with PasswordBox, I can keep my data on my cloud and can export any/all my passwords. However, my 1password trial has expired and I rather not pay $50.
I created https://chronograph.io back in 2014 so people can share accurate, live stopwatches and timers. Other sites "share" by putting the start epoch & duration in a url querystring; Chronograph is one of the first to actually share a session with concurrent edits.
Of the years, it's found a variety of use cases:
- Online scrum meetings.
- Recording starts & stops of races when they're geographically separated.
- Coordination of breaks between language interpreters.
- Syncing of podcast recording with multiple guests.
- Video game twitchers to broadcast when they'll play