Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

What if the interaction were more like OOP - the File class wouldn't necessarily make sense as the top parent.

Would be kind of interesting to call methods on objects rather than read/write files, but it's not immediately obvious to me that that really gains anything over the status quo.

And now that I've written that, I wonder is that what powershell's verb-object does anyway? I've never come close to proficient enough (nor wanted to!) to know.



I have had the (dis)pleasure of learning PowerShell pretty well in the last few months and from that experience I believe forcing different things into the same model doesn't really work. When I read something like "New-Item" I have to take another look and see what it really deals with. Maybe some people think it's "elegant" but I don't agree. It's not like everything that's returned by "New-Item" behaves the same way. .NET is doing it better with static methods like "File.xxxxx" and "Directory.xxxx". You know immediately what you are dealing with.


PowerShell's problem is it's so goddamn wordy and poorly documented. It reminds me of AppleScript, but far worse.


"It reminds me of AppleScript, but far worse."

I wouldn't go that far :-). But you are right. They did good work with the cmdlets but they put a terrible language on top of them.

It would have been much better if they had put an interpreted version of C# (maybe with a few extensions) on top of it.


Agreed, I think F# which already has an interpreter would have been a great and obvious choice. Terse Syntax, amazing type inference, and a great existing community.


This may be interesting. Script languages generally aren’t typed but maybe a language with very good type inference may feel like it’s untyped.


You mostly don't need types in F#, although types often add readability, especially for function signatures (IMHO).

F# will even compile

    let add a b = a + b
    add 1 2;;
as

    val add : a:int -> b:int -> int
    val it : int = 3
or

    let add a b = a + b
    add 1ul 2ul;;
as

    val add : a:uint32 -> b:uint32 -> uint32
    val it : uint32 = 3u
So it will even infer type from the first usage of the function.


Too each their own. With an auto completing IDE and aliases I've never found wordiness to be an issue. Certainly helps when you or others need to read it again. Don't find the documentation lacking either. Even if you do, anything you can do or have access to in C# can be replicated in Powershell.


Some things should not require an IDE? Powershell is a shell so it should be easy to type, no?


The shell has auto complete as well and most of the commonly used cmdlets have aliases for shell usage if you don't want to rely on auto complete. You can also create aliases if you so desire.

Edit: The side benefit of the verbosity is the discoverability of less used commands. Is it groupadd or addgroup? No question in Powershell, it would be Add-Group because of the Verb-Noun standard. Bash has all sorts of inconsistencies that require look-up if you don't use those commands often.


I think Powershell made a big mistake going with verb first. I know I want to do something with group but when I type group there is no auto complete. Now I type “add” and I get dozens or hundreds of suggestions which makes auto complete almost useless. They should have gone with target object first so when I type in group I get all the relevant auto complete suggestions.


I'd suggest the following.

  get-command add*group*
Or for brevity

  gcm add*group*
For reference, the Bash version is this:

  compgen -c | grep group
Edit:

Let's get crazier. You want custom tab completion to focus on the command's noun plus Bash style completion.

  $Function:OriginalTabCompletion = $Function:TabExpansion

  function TabExpansion($line,  $lastWord) {
      if ($line -match ('^!(.*)') {
          $lastWord = $lastWord.trimstart('!')
          Get-command -noun *$lastWord*
      } elseif {
          OriginalTabExpansion  $line $lastWord
      }
  }
  Set-psreadline -chord tab -function MenuComplete

  !group<TAB>


Oh man. Somebody who really knows PowerShell. That's really rare :-)


tell system to tell browser to tell tab with name 'HN' to tell comment with name 'bayareanative' to click button with name 'reply' and ...

Oh hell.


This is very much the model of Win32. Everything thatbexists at least partially in the kernel is exposed as a handle. There are a lot of different types of handles, but all of them can have CloseHandle called on them (maybe there are exceptions? I don't know). Most of them can be used with calls like WaitForSingleObject or WaitForMultipleObjects. So in a way, Windows implements exactly that model.


File descriptors and Windows handles are pretty much the same concept. Just that the former is an int, and the latter is packed into a pointer typedef. Your WaitForMultipleObjects example is not radically different from the poll family of syscalls. (Although there aren't named mutex file descriptors and the like... But Linux has some concepts that remind me of Win32 handle types, such as eventfd(2)).


This is pretty much what I mean. Essentially I'm proposing that the "base class" shouldn't support anything other than close(). You could have objects which don't support read()/write(), but custom verbs with different semantics appropriate to their type. Tape drives (and only tape drives) could support a rewind(), etc.


I think it might be more helpful o think of what semantics could be supported by the everything is a file operations. A tape drive rewind() is just a specialized version of seek(), which any random access object would need to support.

The file metaphor is soooo flexible, so it’s hard for me to think of examples where it breaks down. So, what are some good examples where the file metaphor breaks down? Maybe that’s helpful?


The trouble is, a tape drive can support seek(). But can it support it performantly for all arguments? seek(0, SEEK_SET) is easy. seek(1024, SEEK_CUR) is easy - just read forward a little. But seeking to some arbitrary fixed offset? As far as I'm aware, tape drives are designed to use filemarks for 'searching', not precise offsets.

Of course seek(n, SEEK_SET) could be implemented anyway, in a very un-performant (and tape-wearing manner): by rewinding, and then reading forward n bytes. There's a question of whether the utility of this is desirable when weighed against how surprising it may be to people who don't realise just how bad the performance will be, especially when a tape drive which only supports seek(0, SEEK_SET) can easily have this behaviour emulated on top in userspace by seek(0, SEEK_SET) followed by dummy reads, if you really want it.

read() and write() and seek() prove remarkably versatile, but the niggles come with the fact that different types of file/device on POSIX can have subtly different gotchas with these verbs which, on the face of it, appear to be the same verb. Essentially, I might argue they're not the same verb at all - they just seem similar.

For example, read() from a UDP socket and read() from a normal file have extremely different semantics. If you read() with a 64 byte buffer from a UDP socket, the message is truncated and the remainder of it is lost. This is a very different semantic to reading from a file, where you can read in whatever chunks you like.

I wrote the article upon reflection of precisely this attempt to force everything into the straightjacket of everything-is-a-file that we've had for decades with UNIX. How much code correctly deals with short write()s? "Everything can be expressed as an object on which you can perform read()/write()" can only be true if you ignore the details of a verb's precise semantics, but the precise semantics are important. I think it's fair to argue that write() isn't one verb at this point, but an overloaded verb referring to a set of verbs. Which verb in that set you're invoking is dependent on the type of "file".


The problem, IMO, is less that the file metaphor is not capable of expressing some things and rather that it expresses some things very poorly.

For example, a GPU device doesn't have like a file. You cannot effectively control a GPU via read/write. read/write are excessively slow for anything you'd want to do, including a simple VGA buffer. Almost all operations on a GPU in Linux involve mmap'ing it and then applying ioctl() liberally.

You can do almost everything using the file metaphor, Plan 9 proves it. But it's at times going to be a very poor metaphor that is better at working at all than working well.


This would be interesting for things like robotics and automation; think of a home() command for a mill center, or a lock-all-doors() command for a house, or bedroom-lights-on(), or make-cappacino(). I could go on.

Coffee on the command line sounds interesting.

This could also make GUIs far easier to spin up. A operation on the computer could easily spin up a 'new' GUI that depends on system / operation state using GUI objects available to the entire operating system.


Surely this is just a layer of indirection? It moves the onus for providing implementations of these verbs from software developers to the maintainer of the file type.

Personally I'd rather just stick to the existing analogue of verbs, which we call executables.


But do you really need the open()/close() semantics for everything? What if we wanted to support a hypothetical filesystem with stateless semantics (e.g. a distributed filesystem), then we wouldn't even need close().


You would probably still want a close() to release local resources. A stateless object itself might just NOP the close(), but the local handler might still need to release a reference or two.

I think open/close() are probably the minimal interface.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: