Skip to content
This repository was archived by the owner on Sep 12, 2018. It is now read-only.

Have the EDN parser store keywords and symbols as rich types. Fixes #154.#163

Closed
rnewman wants to merge 1 commit into
rustfrom
rnewman/edn-keyword
Closed

Have the EDN parser store keywords and symbols as rich types. Fixes #154.#163
rnewman wants to merge 1 commit into
rustfrom
rnewman/edn-keyword

Conversation

@rnewman
Copy link
Copy Markdown
Collaborator

@rnewman rnewman commented Jan 11, 2017

This demonstrates some unpleasantness with name reuse between enums and structs; the verbosity encourages the use of a test helper function.

Rust also doesn't infer types as much as I'd like, so you can't, say:

let v = keyword(":foo/bar");     // Parser function: returns a Value, specifically Value::Keyword
assert_eq!(v.unwrap().0, keyword::Keyword::namespaced("foo", "bar"));

because Rust doesn't know which enum value v will be.

I could 'inline' keyword::Keyword into Value… but then you can't define functions that work only on keywords; they'd all have to accept a Value and handle the other cases.

@rnewman rnewman self-assigned this Jan 11, 2017
@rnewman rnewman requested a review from ncalexan January 11, 2017 22:53
Copy link
Copy Markdown
Member

@ncalexan ncalexan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bombs away for commits 1 and 3; I think a little time will improve commit 2 significantly.

Comment thread edn/src/edn.rustpeg
namespace_separator = "/"
keyword_char_initial = ":"

// TODO: More chars here?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment still relevant? _, *, and - seem like good name{space} chars...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided not to attack that yet. Start narrow and broaden later.

Comment thread edn/src/edn.rustpeg Outdated
}
keyword -> Value
= keyword_char_initial ns:( kns:$(keyword_namespace) namespace_separator { kns })? n:$(keyword_name) {
Value::Keyword(keyword::Keyword::perhaps_namespaced(ns, n))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a place where From/Into can help?

Comment thread edn/src/types.rs Outdated
Text(String),
Symbol(String),
Keyword(String),
Keyword(keyword::Keyword),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I really think we want impl From<Into<String>> for keyword::Keyword ... if that's legal, or From<String> if not. Then we can still construct Value::Keyword the easy way.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two reasons why not:

  • Not all strings are valid keywords.
  • Most constructions are namespaced, which makes that approach confusing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latter "namespaced" refers to Rust's namespacing, not EDN's, right? I think you could make impl TryFrom<Into<String>> ... and Keyword(":foo/bar") work.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, namespaced is the specific constructor function for :foo/bar as opposed to :bar.

Keywords are essentially in two disjoint sets: those with namespaces and those without.

I modeled this as an Option<String> for the namespace, because I didn't want yet another layer of enums. I modeled this as two constructor functions with one helper (perhaps_namespaced) to avoid passing None in places.

This is a spot where you can use some trait tricks to allow coercion from &str to Some<&str> to Some<String> within the same signature, but that would make the constructors relatively more complicated.

Comment thread edn/tests/tests.rs
use edn::parse::*;

// Helper for making wrapped keywords with a namespace.
fn k_ns(ns: &str, name: &str) -> Value {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's spend some time to learn Into and From; these are solved problems in Rust-land.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not worth doing the namespace bit in the parser, but instead doing it in the constructor. Since you have to check for empty strings anyway, might as well have one place that encodes the rules.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use From: those conversions cannot fail, and not all strings can be turned into a keyword.

Indeed, even TryFrom returns a Result, and thus complicates matters.

This is the root of parsing, and why I picked this division: the struct and its constructors aren't failable (they include panicking validation, but that's another matter), whereas the parser itself will refuse to parse a malformed keyword.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this is fine for now.

@rnewman rnewman force-pushed the rnewman/edn-keyword branch from f4e0860 to 1025fe7 Compare January 12, 2017 01:11
@rnewman rnewman changed the title Have the EDN parser store keywords as a rich type. Partially fixes #154. Have the EDN parser store keywords and symbols as rich types. Fixes #154. Jan 12, 2017
Comment thread edn/src/edn.rustpeg

use std::collections::{BTreeSet, BTreeMap, LinkedList};
use std::iter::FromIterator;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something I miss from Java was a rough standard of individual imports, sorted alphabetically, inserted by some refactoring and automatically folded by the editor. Minimal code churn which the developer didn't need to think about.
I suspect that rust tooling will catch up shortly. So nothing to do now, mostly thinking aloud.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I decided to do much what we've been doing in Swift and Java: split std out, sort all alphabetically within that division.

@rnewman
Copy link
Copy Markdown
Collaborator Author

rnewman commented Jan 12, 2017

a152e60

@rnewman rnewman closed this Jan 12, 2017
@rnewman rnewman deleted the rnewman/edn-keyword branch January 12, 2017 17:24
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants