Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RelURI, AbsURI #37

Open
bapcyk opened this issue Jan 2, 2018 · 6 comments
Open

Add RelURI, AbsURI #37

bapcyk opened this issue Jan 2, 2018 · 6 comments

Comments

@bapcyk
Copy link

bapcyk commented Jan 2, 2018

UriAuth is under Maybe - as I understood due to wrap relative/absolute URIs under one type. If I have abs. URI I will propagate those Maybe anywhere. Better may be to distinguish abs. and rel. URIs - with different types. When I parse rel. URI (reference) - no any auth. In this case combining URIs will be checked on type level: rel+rel, abs+rel, but no abs+abs.

@ezrakilty
Copy link
Contributor

OK, so there are two reasons why the uriAuthority field currently has to be a Maybe.

(a) Because we support parsing both URIs and URI references through the same interface,
(b) Because authority is not a required component of a URI, in general (some schemes don't use it, even for what you'd call an "absolute" URI).

I agree with you that it might be better if "URI references" (relative URIs) were a different type, if only to type-check against combining two "non-relative" URIs. We can consider adding new interfaces to support that idea, but we are stuck with supporting the existing types. We can look at some concrete proposals together for an additional set of types and functions; I'm a little skeptical that it doesn't wind up being a complete fork of the whole interface, but let's explore it.

If the real goal is to get to a place where you have an "absolute" URI type that doesn't allow Nothing for the authority (and so doesn't require casing on it throughout the code) then we need some additionally trickery, to somehow limit to one scheme or to schemes that require authority. It seems like that, too, will have to be a whole separate interface. I could imagine something like a package Network.URI.HTTP, for example, which defines a type just for (absolute) http: URIs, perhaps with a parallel set of functions, and which can be created from today's URI object. I haven't thought about the details too much. Would you be interested in sketching up a proposed interface? I make no guarantee about putting it in the main package, but if it doesn't fit there it could certainly go in a second package.

@bapcyk
Copy link
Author

bapcyk commented Jan 4, 2018

IMHO good is t dispense with little blood. For example. URI now is type for rel and abs URIs. It is reflected in the name. If somebody needs more strict it can use 2 additional types: AbsURI, RelURI. How to keep different APIs (1st for URI, 2nd for RelURI/AbsURI) - I don't know. In OOP I can imagine solution: each AbsURI and RelURI are still URI, so old API can be for backward compatibility (and to work with URI as now, with AbsURI/RelURI too - they are URIs too) and to have new, more strict API, where some run-time checks will moved to types restrictions. Modules may be: Network.URI, Network.URI.Strong or something else. But I'm not sure how to do it better - it's only idea, and maybe not better one :)

@ezrakilty
Copy link
Contributor

The idea of strongly typing relativeTo and relativeFrom, to force relative URIs in some places and absolute ones in other places, seemed intriguing, so I mocked it up in this branch:

https://github.com/haskell/network-uri/tree/strong

It takes the form of a second module, Network.URI.Strong, that differentiates between an "absolute" type of URI reference, URI, from a "relative" one, RelativeReference. Then it redeclares all of the functions from Network.URI except that

(a) the relativeTo and relativeFrom functions have more precise types,
(b) the predicates uriIsAbsolute go away, because they dynamically detect what is now statically represented,
(c) nonStrictRelativeTo goes away because it tries to be non-strict in exactly the way this module is trying to be strict,
(d) the deprecated functions are thrown away
(e) I carried over nullURI, but maybe it makes no sense. It constructs something that violates the typing discipline. Moreover, the utility of nullURI was to help in constructing the URI objects directly, component by component. But componentwise construction is now more difficult (because there's one extra level of wrapping) and unwise (because we don't have a way of guaranteeing that the construction abides by the typing discipline. So this part would probably need some more thought.

If this project were to move forward, it would be necessary to make the new module fully documented and tested. It becomes a new, parallel, interface that needs to be perpetually kept in sync with the traditional one. I'm not too keen on that, but I offer it up for comment.

(By the way, the word "absolute" is used in the RFC in a slightly surprising way: It requires that there be no fragment component (as well as the more expected requirement that the scheme is specified, which captures the intuition that an absolute URI is "rooted" and "not relative to" some other URI). In the Network.URI module, that sense of "absolute" is used in some places, but the uriIsAbsolute/uriIsRelative functions just go by the presence of a scheme.)

@bapcyk
Copy link
Author

bapcyk commented Jan 12, 2018

Good news, super!

RFCs "absolute", maybe, is more relevant from content point of view: relative URL in this case refers to something inside itself (serving a part vs. serving a whole)... So, I'm not sure what will be better terminology is this case, if RFC already reserves "absolute" word :)

@Porges
Copy link

Porges commented Mar 4, 2018

I came here to ask for the same thing 🙂

Another option is to provide relative/absolute like how the path package does it, so we'd have (Strong.)URI Abs and URI Rel and an operator </> for combining relative URIs. I think this could work quite nicely!

@Porges
Copy link

Porges commented Mar 4, 2018

Here's a sketch...

{-# LANGUAGE EmptyDataDecls #-}
module Network.URI.Strong 
    ( URI
    , Abs, Rel
    , getURI
    , parseRelativeURI
    , parseAbsoluteURI
    , (</>)
    )
where

import Data.Typeable
import qualified Network.URI as U

data Abs deriving (Typeable)
data Rel deriving (Typeable)

newtype URI t = URI { getURI :: U.URI }
    deriving (Show, Eq, Ord)

parseRelativeURI :: String -> Maybe (URI Rel)
parseRelativeURI = fmap URI . U.parseRelativeReference

parseAbsoluteURI :: String -> Maybe (URI Abs)
parseAbsoluteURI = fmap URI . U.parseAbsoluteURI

(</>) :: URI t -> URI Rel -> URI t
(URI base) </> (URI rel) = URI (rel `U.relativeTo` base)

This would be sufficient for my use (since you can access the scheme etc by calling getURI first).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants