
On 20 October 2010 13:09, Simon Peyton-Jones
Yes, you can freely use Foo/unFoo. There's no runtime penalty. (In the jargon of GHC's intermediate language, Foo and unFoo translate to *type-safe casts*, which generate no executable code.
That includes the 'newtype deriving' stuff too, and hence your uses of fromInteger etc.
Oh wow, excellent!
No, this isn't optimised. The trouble is that you write (map Foo xs), but GHC doesn't know about 'map'. We could add a special case for map, but then you'd soon want (mapTree Foo my_tree).
What you really want is to say is something like this. Suppose my_tree :: Tree String. Then you'd like to say my_tree ::: Tree Foo meaning "please find a way to convert m_tree to type (Tree Foo), using newtype coercions.
The exact syntax is a problem (as usual). We have the technology now. The question is how important it is.
I don't know whether it's so important for me at least. I was just
interested in how much optimisation it did.
On 20 October 2010 17:58, Gregory Crosswhite
On 10/20/10 4:09 AM, Simon Peyton-Jones wrote:
No, this isn't optimised. The trouble is that you write (map Foo xs), but GHC doesn't know about 'map'. We could add a special case for map, but then you'd soon want (mapTree Foo my_tree).
How about a special case for fmap? That seems like it should handle a lot of cases.
Personally I haven't had much use for mapping newtype constructors/unconstructors. My personal use case of newtypes unwrapping/wrapping is in passing them to functions and in record fields, not so much unwarpping them inside data types and trees across the board. -- | Assign a review for a submission to a user. assignReview :: TrackId -> UserId -> SubmissionId -> Model () assignReview tid uid sid = do insert T.review $ F.uid <<- unUserId uid # F.submission <<- unSubmissionId sid # F.trackId <<- unTrackId tid This is the simplest function I could find. However, consider if in my busy hacking I accidentally get the argument order incorrect in the definition, or the call, with newtypes I can't mismatch them. This has stopped me doing bad things a few times in my haste. Another nice thing I've found is combining them with view patterns: -- | Submit a review. submitReview :: UserId -> SubmissionId -> [ReviewField] -> Model () submitReview (unUserId -> uid) (unSubmissionId -> sid) rs = do forM_ rs $ \ReviewField{..} -> ... The next ten lines or so use uid and sid. This makes the submitReview function kind of "guarded" from being given the wrong values. Inside it can do what it wants. You might point out that now that I've unwrapped them I can pass them to some other function willy nilly, but of course if a function needs a submission id, then it needs a SubmissionId. So I find this really nice. And of course I use the deriving extensively: newtype SubmissionId = SubmissionId { unSubmissionId :: Int } deriving (Show,Num,Eq,Ord,Enum,Integral,Real,ShowConstant,JSON) I have a types file with 46 newtypes, zero type aliases, and ah, 47 data types. GHCi gets kind of slow when dealing with it. My solution is to have an Emacs shortcut to build the project with cabal every so often so that GHCi can load the .o files in an instant.