isaacschemm: A cartoon of myself as a snail (snail8)
Entry tags:

Nice Little Things in Visual Basic .NET

Being (loosely) based on the original Visual Basic, it's not surprising that Visual Basic .NET has features specifically targeted towards Windows Forms development. When I want to make a quick GUI app to run on my PC, I often find it easiest to build the main code in a C# or F# library, and to build a thin frontend layer in VB.NET, for two reasons: the incredibly aggressive (in a good way) auto-formatting that keeps me from being distracted by code style, and the nice set of quality-of-life helpers the language gives you for this exact use case.

Read more... )
isaacschemm: A cartoon of myself as a snail (snail8)
Entry tags:

Breaking podcasts into chunks for burning to CD

Sometimes I want to listen to a podcast on a TV, and sometimes the easiest way to do that is by burning it to a CD (either a CD-ROM with MP3 files, or - if it fits - a normal audio CD). Not every CD player has the greatest seeking features, though. I've got one whose fast-forward is more of a normal-speed-forward, and another that actually goes forward when rewinding an MP3 at the slowest level.

I figured one way of working around these issues - which could come into play for podcast episodes that are, like, an hour long - would be to split the podcast into segments of five minutes each. You'd need to make a normal audio CD (which means a limit of 75 or 80 minutes or so), but you could have gapless playback, while also being able to use track selection to go forward and back in chunks.

Yesterday I put together a small Windows application to help with this. It's called Cue Sheet Generator, and it takes in one or more audio files and converts them to either a set of .wav files (to burn with Windows Media Player Legacy or another app with gapless burning support) or a .wav/.cue pair (which ImgBurn and other such apps can handle).

The main program logic is small enough to fit into this post. I wrote it in VB.NET (there's nothing here C# couldn't do, I'm just tired of looking at curly brackets), and I thought it might be helpful to annotate it.

Read more... )
isaacschemm: A cartoon of myself as a snail (snail8)
Entry tags:

Give me a yield break

Working in .NET, I've been in a situation where I need to implement a function in an interface that's supposed to return an IEnumerable<T>. (In some cases, I'd argue that IReadOnlyList<T> might make more sense - this way, the caller knows it's not going to be a lazily-evaluated sequence, and you can still return a .NET list, F# list, or array - but I digress).

Maybe the most obvious way to do this is by calling a function that explicitly returns an empty enumerable for you:

IEnumerable<string> SampleInterface.getAll() => Enumerable.Empty<string>();

In F#, it would be even shorter, because of the aggressive type resolution:

interface SampleInterface with
    member _.getAll() = Seq.empty

There are other clever ways to do the same thing, though, and it might just depend on what you think is the clearest or most readable - which might just mean keeping it consistent with the code around it. First, you can always expand out the function (because clearer isn't always shorter - I think it really does depend on the context of what's around it):

IEnumerable<string> SampleInterface.getAll() {
    return Enumerable.Empty<string>();
}

But there's also something clever you can do here, if you want to think of your code in a different way - where instead of resulting in "an empty list", it results in "no elements". C# lets you build iterator functions, where your code defines an IEnumerable<T> (and runs every time the resulting object is enumerated). Any function with a yield return or a yield break is treated in this way by the compiler. This means you can implement a function that returns "no elements" just by doing this:

IEnumerable<string> SampleInterface.getAll() {
    yield break;
}

It's a bit different in VB.NET, where iterator functions are denoted explicitly - so the yield break isn't needed:

Public Iterator Function getAll() As IEnumerable(Of String) Implements SampleInterface.getAll

End Function

That is, in a very literal sense, a function that returns no elements!

Funny thing is that there's no real equivalent to an empty iterator function in F# (not that you'd need it); the compiler won't allow a seq { } workflow without any elements in it, and suggests you use Seq.empty or the empty list [] instead.