-
Notifications
You must be signed in to change notification settings - Fork 9
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
Morte #6
Comments
As for code that requires unbounded recursion, Morte code is able to generate any code. I’d be perfectly happy with a guaranteed-safe That doesn’t necessarily complicate the programmer’s life. For example, Haskell’s |
The unique features of
The main disadvantages of
This makes it significantly more difficult to program directly in
Much fewer limitations than you think! The key is that recursive data structures and functions can be mechanically translated into non-recursive equivalents. There's an excellent paper which describes the algorithm behind this and the original post on What's really neat is that the static linking property I mentioned holds even in the presence of "recursive" data types. For example, if I have something like the following mutually recursive Haskell data types: data Even = Zero | SuccE Odd
data Odd = SuccO Event ... Note that just because it is possible to transform recursive programs into non-recursive versions, doesn't mean that it is easy. In fact, it's actually a bit difficult for more complex algorithms because you have to handle every single possible corner case. If you want to see some really hairy example code, I have an implementation of binary numeral arithmetic in this directory and the implementation of The most important guarantee that the lack of recursion gives is that it's always safe to normalize expressions (i.e. you can safely inline everything) because the inlining process is guaranteed to terminate. This is why To make this concrete, here is an example of something that $ # First, some preliminary "imports" to make the code less verbose
$
$ # There is a Prelude of `morte` expressions which you can browse here:
$ # http://sigil.place/talk/0/
$
$ echo '#http://sigil.place/talk/0/Bool' > Bool
$ echo '#http://sigil.place/talk/0/Bool/False' > False
$ echo '#http://sigil.place/talk/0/List' > List
$ echo '#http://sigil.place/talk/0/List/Cons' > Cons
$ echo '#http://sigil.place/talk/0/Bool/and' > and
$
$ # This optimizes `\bs -> and (False:bs)` to `\bs -> False`
$
$ echo '\(bs : #List #Bool ) -> #and (#Cons #Bool #False bs)' | morte
∀(bs : ∀(List : *) → ∀(Cons : ∀(head : ∀(Bool : *) → ∀(True : Bool) → ∀(False : Bool) → Bool) → ∀(tail : List) → List) → ∀(Nil : List) → List) → ∀(Bool : *) → ∀(True : Bool) → ∀(False : Bool) → Bool
λ(bs : ∀(List : *) → ∀(Cons : ∀(head : ∀(Bool : *) → ∀(True : Bool) → ∀(False : Bool) → Bool) → ∀(tail : List) → List) → ∀(Nil : List) → List) → λ(Bool : *) → λ(True : Bool) → λ(False : Bool) → False However, there are some things that Also, remember that hugely complex implementation of $ echo "#http://sigil.place/talk/0/Bin/(+) 0" | annah | morte
∀(b2 : ∀(Bin : *) → ∀(Zero : Bin) → ∀(One : (∀(Bin_ : *) → ∀(Nil_ : Bin_) → ∀(Zero_ : Bin_ → Bin_) → ∀(One_ : Bin_ → Bin_) → Bin_) → Bin) → Bin) → ∀(Bin : *) → ∀(Zero : Bin) → ∀(One : (∀(Bin_ : *) → ∀(Nil_ : Bin_) → ∀(Zero_ : Bin_ → Bin_) → ∀(One_ : Bin_ → Bin_) → Bin_) → Bin) → Bin
λ(b2 : ∀(Bin : *) → ∀(Zero : Bin) → ∀(One : (∀(Bin_ : *) → ∀(Nil_ : Bin_) → ∀(Zero_ : Bin_ → Bin_) → ∀(One_ : Bin_ → Bin_) → Bin_) → Bin) → Bin) → b2 In other words, I think the easiest way to answer your general concern about recursion is if you give me a simple problem that you think is difficult to encode in a non-recursive language and I actually write up a solution for it in One of the goals of $ echo "#http://sigil.place/talk/0/Bin/(+) 1" | annah | morte > inc ... and then use that within a future computation: $ echo "#inc 3" | annah | morte
∀(Bin : *) → ∀(Zero : Bin) → ∀(One : (∀(Bin_ : *) → ∀(Nil_ : Bin_) → ∀(Zero_ : Bin_ → Bin_) → ∀(One_ : Bin_ → Bin_) → Bin_) → Bin) → Bin
λ(Bin : *) → λ(Zero : Bin) → λ(One : (∀(Bin_ : *) → ∀(Nil_ : Bin_) → ∀(Zero_ : Bin_ → Bin_) → ∀(One_ : Bin_ → Bin_) → Bin_) → Bin) → One (λ(Bin_ : *) → λ(Nil_ : Bin_) → λ(Zero_ : Bin_ → Bin_) → λ(One_ : Bin_ → Bin_) → Zero_ (Zero_ Nil_)) That result is how the number 4 is encoded in |
@Gabriel439 Thanks for your detailed reply :) . I was hoping @jbenet would have responded by now, but he seems to be busy with things at the moment. So, in the meantime, let's discuss getting a proof of concept running. The easiest way of doing this would probably be to get morte to be able to resolve ipfs addresses ( $ ipfs-morte /ipfs/...
Compiling...
Result: ... |
I can do this without making any changes to the The only thing I'm missing is how to translate |
@Gabriel439 You just prefix the paths with the address of the gateway server, for example:
See https://ipfs.io/docs/getting-started/ for more information. |
Sorry for the slow turnaround, but I finally wrote up a simple https://github.com/Gabriel439/Haskell-Morte-Library/blob/ipfs/exec/Ipfs.hs I was able to upload a Morte expression to a local IPFS daemon (temporarily hardcoding the gateway to
The error message indicates that the server sent a TLS packet with an invalid contents type ( |
|
Alright, I got it working just by using What's the difference between the public and private interfaces? |
is the api client setting origin/referrer headers? cc @diasdavid @dignifiedquire @davidar thoughts? this is a Haskell api client
|
Alright, I added command-line options to parametrize the $ # Uploading `(&&)`
$ echo "λ(x : ∀(Bool : *) → ∀(True : Bool) → ∀(False : Bool) → Bool) → λ(y : ∀(Bool : *) → ∀(True : Bool) → ∀(False : Bool) → Bool) → x (∀(Bool : *) → ∀(True : Bool) → ∀(False : Bool) → Bool) y (λ(Bool : *) → λ(True : Bool) → λ(False : Bool) → False)" | ./ipfs add
added QmaHXo2mYsdmDf2V35QHVTuUCAjKzwbtd419BWnbmCkUJB QmaHXo2mYsdmDf2V35QHVTuUCAjKzwbtd419BWnbmCkUJB
$ # Uploading `True`
$ echo "λ(Bool : *) → λ(True : Bool) → λ(False : Bool) → True" | ./ipfs add
added QmTBfdpJx6MLyxadZLRhG7LYcrmjxgCtWJGef4VxG1mo8v QmTBfdpJx6MLyxadZLRhG7LYcrmjxgCtWJGef4VxG1mo8v
$ # Uploading `False`
$ echo "λ(Bool : *) → λ(True : Bool) → λ(False : Bool) → False" | ./ipfs add
added QmTvtqwNJzNGWUVMnJJErYTKWPjhsVgeN2g9vkmtXu3R8a QmTvtqwNJzNGWUVMnJJErYTKWPjhsVgeN2g9vkmtXu3R8a
$ # Uploading "dynamically linked" `(&&) True False` expression
$ echo "
> #http://ipfs.io/ipfs/QmaHXo2mYsdmDf2V35QHVTuUCAjKzwbtd419BWnbmCkUJB
> #http://ipfs.io/ipfs/QmTBfdpJx6MLyxadZLRhG7LYcrmjxgCtWJGef4VxG1mo8v
> #http://ipfs.io/ipfs/QmTvtqwNJzNGWUVMnJJErYTKWPjhsVgeN2g9vkmtXu3R8a
> " | ./ipfs add
added QmR9XR1F15LQnqB12pcimzySkX9fWBp8Xc43RsFCoopbeZ QmR9XR1F15LQnqB12pcimzySkX9fWBp8Xc43RsFCoopbeZ
$ # Retrieve and compute `(&&) True False` from the public `ipfs.io` server
$ morte-ipfs /ipfs/QmR9XR1F15LQnqB12pcimzySkX9fWBp8Xc43RsFCoopbeZ
∀(Bool : *) → ∀(True : Bool) → ∀(False : Bool) → Bool
λ(Bool : *) → λ(True : Bool) → λ(False : Bool) → False The |
It also just occurred to me that $ echo "#http://ipfs.io/ipfs/QmR9XR1F15LQnqB12pcimzySkX9fWBp8Xc43RsFCoopbeZ" | morte
∀(Bool : *) → ∀(True : Bool) → ∀(False : Bool) → Bool
λ(Bool : *) → λ(True : Bool) → λ(False : Bool) → False |
@Gabriel439 this is fantastic!! and very exciting!
|
I think the For the write path I just need to know what APIs are available for uploading to I also would like to know how to do web-based integration. That would be the more ideal API since it doesn't require any installation from end users. |
Hooray! @Gabriel439 yes, I agree this doesn't buy us much yet, but the next step is to |
So if you need a Javascript binding to If that doesn't work, an alternative approach is just to port the |
The markup editor @jbenet linked earlier uses ghcjs |
Oh sweet, then this should be much easier than I thought! |
yay unixy tools :) |
cc @cleichner |
(Tangential topic. I may break this out into own discussion.) It would be really useful to make it easier to write ubiquitous distributed authenticated protocols (via authenticated data structures and say, js). We could do this with haskell libraries and/or on top of morte, and ghcjs. Though we could also see about doing a similar thing with OCaml, to use lambda-auth. A quick googling shows there's several ways of compiling OCaml to JS, but i've yet to look deep enough to understand the relevant security guarantees/constraints. (Or even OCaml -> Morte, if that even makes any sense?) @amiller are you interested in this? (take a look at what @Gabriel439 has achieved with Morte). |
cc @joeyh |
I am rereading this thread and Morte seem like an awesome language for the job but until we try it we won't know if it is the right tool for a transformations in IPFS. Example use cases that I can see IPFS transformation engine to perform:
There are more of them but those are once that I heard people rise interest in them. I still don't fully understand limitations of Morte but it seems that it shouldn't be a problem and I am just making sure it is so. Also how hard would it be to re-implement Morte's execution environment in other language (Go, JS)? |
If you do that then you're mostly using the language as a code distribution mechanism and not as an execution engine |
From comments by @Gabriel439 on ipfs/ipfs#47
I'm creating this issue to help coordinate integrating
morte
with IPFS. Personally I think a language likemorte
would be a great fit for embedding computable data in IPFS.@Gabriel439 Playing devil's advocate, I have a few questions: How does this improve on distributing regular (Safe)Haskell or pure Lisp source code (or BLC) over the internet? What limitations does the lack of recursion have in practice, and is this outweighed by the guarantees it allows you to make about the code? What suggestions would you make in terms of being able to use code that requires unbounded recursion?
CC: @jbenet @ion1
The text was updated successfully, but these errors were encountered: