[m-dev.] Unique Modes and Transaction Logic
Marcus Troyka
aeonioshaplo at gmail.com
Mon Jul 21 06:13:44 AEST 2014
First I'd like to start by taking back anything I might have implied
about OCaml being pure. Their 'reference cells' which are used for
mutable variables are basically explicit pointers with no type safety.
OCaml also has some other issues that don't make sense, but that's
another story.
On 07/20/2014 04:00 AM, Mark Brown wrote:
>
> Thanks for sharing your thoughts in any case.
>
> I'm still just guessing what problem you are solving, though. The
> first of the papers you referenced has two practical examples, one on
> financial transactions and one on robot actions. The first example can
> be expressed in Mercury using unique modes (section 5.1 of the
> reference manual), and the second using "mostly unique" modes (section
> 5.2) and committed choice non-determinism (sections 6.4 and 6.5). So
> Mercury's modes appear to be sufficient to address the problems raised
> by this paper, at least.
Well, mostly I was just considering mercury as a whole, as it applies to
AI, databases, and general programming. The particular problem /I'm/
trying to solve is a bit different though. Basically, I don't like the
way most operating systems, kernels, and desktop environments are
designed in general, and I don't like the languages that they're built
with for various reasons. Updates tend to break as many things as they
fix, and every OS seems to have as many disadvantages as advantages. I
figure to start with I should address the most fundamental problems in
OS and general program design, and then choose better algorithms and
organization from there. Currently there are no pure declarative
languages which are pure enough, usable enough, or efficient enough for
that sort of usage, but not because it isn't possible.
> Yes, perfect static determinism checking is not possible in general;
> see the discussion in section 6.3. I dare say it is not possible to
> prove much about `assert'.
In logic languages, no. In functional languages it's pretty easy (and
all functions have to be det anyway).
> (Transaction logic is not tied to `assert', though; you can choose a
> different set of elementary operations. If instead of `assert' there
> was an elementary operation like Mercury's map.set, that replaced any
> existing row that had the same input values, then proving determinism
> can be easy.)
>
I was hoping to end up with something that looked more like logic, but
that doesn't seem too possible.
> I think Paul just meant that meta-interpretation like Prolog or Scheme
> is not one of Mercury's goals, not that building
> compilers/interpreters in general is not a goal.
It's very possible that transaction logic wouldn't translate well to
fully compiled code. Given that mercury has other ways of handling those
sorts of issues it doesn't seem like it should be necessary anyway.
> For my money, the biggest advantage of logic programming over
> functional programming is that you can have multiple output arguments.
> In functional syntax, if a procedure has more than one output you are
> forced to either cram everything into the one return value, or else
> make use of side effects.
I should mention that C carries that same limitation, but in practise it
doesn't usually matter. The way side effects are used in OO is a bit
different, but in functional programming a major advantage is that since
functions only produce one output value (and are always det) they can
use evaluate-in-place semantics, which are easy to read and understand.
With side effects, since unique outputs can't be evaluated directly,
it's not so much of an issue if a function returns a unique output as
well as a (single) standard one, such as with io.read_line_as_string(X,
!IO), and you can still pass "!IO" as an input. Side effects in pure
functional code face somewhat different issues than in pure logic, but
that's another topic.
On 07/20/2014 04:16 AM, Paul Bone wrote:
> I think this depends on your definition of an object. If this definition
> says that an object has state, and state may be modified, then unless you
> make the effect of modifying state part of a method's declaration (see effect
> typing[1]) then yes, class-based OO programming has side-effects.
The main difference between an 'object' and a 'module' is that an object
can carry persistent local state and have instances, whereas modules
only provide procedures (with exclusively local variables).
>
> Note that in prototype-based OO programming[2, 3] modifying an object creates
> a new object rather than destructively modifying the existing object, so it
> does not need to have side effects. IMHO prototype-based OO programming has
> it's own issues, in particular whether a given object implements a given
> method is not decidable at compile time, so it must be dynamically checked
> rather than statically checked.
>
> 1. http://disciple.ouroborus.net/
> 2. http://selflanguage.org/
> 3. http://old.jorgenschaefer.de/software/prometheus/prometheus.html
>
That's not actually how prototyping works, which varies from language to
language. The main (useful) features of prototyping are delegation
(which is very elegant) and that reusable code (inheritance) can be
extracted from (whole text copied) prototypes /after/ the code has been
implemented rather than having to consider it beforehand.
In L'issac and javascript, a prototype's definition is considered to be
it's first instance, but this leads to confusing behavior (what happens
if you clone the object after its instance variables have been
modified?). In self, objects can be modified procedurally at runtime
(self is interpreted) and you can do all sorts of nonsensical (and
impure) things with objects in self. In some prototyping languages
(self, L'issac) everything (including ints and other discreet types) is
considered to be an object, and/or a slot, and/or a method, which makes
the code rather ambiguous (like 'let' statements in ML, except perhaps
worse).
However, the way you described it sounds just about brilliant. I think I
vaguely thought of doing something like that, but the way you stated it
makes everything much clearer. I think the way I would do that (in
functional-objective pseudocode) would be something like:
:- prototype Foo
:- interface
+method new(int argX, int argY) = Foo.
-method set_x(int someDescriptiveName) = Foo.
//I think named variables in declarations would be awesome for
readability. Especially when considering closed source libraries that
only provide headers.
assert int x.
assert int y.
//declare two instance variables x and y; note that I'm using assert
differently here than in logic
:- implementation
set_x(a) =
(
[Foo new(a, y)]
).
//objective-C style
//Note that "Foo" might be omitted here as a convenience like "self" is
omitted in the self language.
new(argX, argY) =
(
assign x = argX
and
assign y = argY
then
[super new]
).
//maybe 'in' instead of 'then' here, confusing since pure functions have
no non-local variables.
// "assign" here is used to mean assign-once.
//and then you can call something like:
let newObject = [oldObject set_x(3)]
and then just let CTGC deal with the space leak that would otherwise
occur. Correctly dealing with the peculiar locality of instance
variables would be the main challenge, I think. Or at least doing so
while keeping the code readable and convenient.
Hmm!
Cheers
-MarcT
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurylang.org/archives/developers/attachments/20140720/72b486b9/attachment.html>
More information about the developers
mailing list