Hi all,
It seems to me that is an example of the more general problem of how
to represent structured data in a language with only atomic values and
binary relations (like RDF). In languages with algebraic data types, it
would be very easy; eg in Prolog or Mercury, I would define a type
for lists of signals, like this
playlist ---> pl(signal, playlist_tail).
playlist_tail ---> plt(signal, justification, playlist_tail) ; nil.
then a particular list can be written as a single expression using the functors
pl/2, plt/3 and nil/0, eg
PL=pl( sex_machine, plt(good_foot, same_maker(sex_machine, good_foot), nil).
same_maker(A,B) :- maker(A,M), maker(B,M).
Here, I've assumed that justication is simply a proposition (eg a Prolog goal)
that is true and is an explanation of why this item follows the previous.
In Haskell, the data type would be:
data Playlist t = PL t (PlaylistTail t)
data PlaylistTail t = PLT t Justification PlaylistTail | PLTNil
where I've thrown in some polymorphism over the item type just for good
measure.
Clearly, the problem with RDF is that we can't write down compound terms
like plt( good_foot, its_tuesday, nil) but have to describe them in a reified
way using only binary relations. To do this in the most obvious and bloody
minded way is quite laborious (which reminds me of writing programs in
assembly language instead of a high level language), eg if
X=pl(sex_machine, plt(good_foot, its_tuesday, nil)) is a playlist, then:
functor(X, pl/2),
pl_head(X, sex_machine),
pl_rest(X, Y),
functor(Y, plt/3),
plt_head(Y, good_foot),
plt_justification(Y, its_tuesday),
plt_rest(Y, Z),
functor(Z, nil/0).
Here I've introduced a separate binary relation for each named slot of
each functor. This more or less corresponds to Kurt's initial suggestion,
except that the justification is attached to the *following* item rather
than the *preceding* one, which means that a playlist of one item needs
no justification. Also, according to my definition at the top, a playlist
must have at least on item. If we are being pedantic, then the class
structure implied by all this is quite involved: each constructor/functor
for each type is a separate class:
Root
Playlist (valid properties are head and rest)
PlaylistTail (NO properties)
PlaylistTail_PLT3 (props are head, justification and rest)
PlaylistTail_Nil (no properties)
NB, a playlist_tail is *not* a playlist, and the properties head, justification
and rest can only apply to the subclass PlaylistTail_PLT3 and not to the
class PlaylistTail. This means that logic inferences can work correctly
in an open world - a playlist reaches its end when we *know* that the
list item is a PlaylistTail_Nil, and not simply when we can't find any
'rest' property. In this particular case, we *could* collapse Playlist_PLT3
into PlaylistTail, eliminate Playlist_Nil, and have a special individual
playlist_tail_nil to denote an empty PlaylistTail (ie just like rdf:List and
rdf:nil), but in the more general case of an algebraic data type with more
than one constructor/functor with arguments, this option would not be
available. For example, if a Playlist could call another playlist like a
subroutine, with no justification required, then we would have an
algebraic type like this:
playlist_tail ---> plt( signal, justification, playlist_tail)
; inc( playlist, playlist_tail)
; nil.
In this case, we would need the two separate subclasses PlaylistTail2_PLT3
and PlaylistTail2_Inc2 because they would have different arguments/properties.
Kurt's 'how long is the playlist' question is answered easily enough
in Prolog form with pattern matching:
playlist_length(pl(_,PLT), N) :- playlist_tail_length(PLT, M), N is M+1.
playlist_tail_length(plt(_,_,PLT), N) :- playlist_tail_length(PLT, M), N is M+1.
playlist_tail_length(nil, 0).
but without pattern matching and using only binary predicates (ie RDF form)
as in the earlier example, it would be
% length of playlist
pl_length(PL, N) :-
functor(PL, pl/2),
pl_rest(PL,PLT),
plt_length(PLT, M),
N is M+1.
% length of playlist_tail
plt_length(PLT, 0) :- functor(PLT, nil/0).
plt_length(PLT1, N) :-
functor( PLT1, plt/3),
plt_rest( PLT1, PLT2),
plt_length(PLT2, M),
N is M+1.
So it's a bit fiddly but fairly mechanical.
Yves suggested using Track and track_number to model the sequence. This
would be an example of the more general idea of modelling a sequence as
as set of (index,item) pairs (ie a binary relation), but to do it here, we'd need
a *ternary* relation seq_index_item/3, where seq_index_item(Playlist, N, Thing)
means 'Thing is the Nth item in Playlist'. It has to be ternary because, as Kurt
pointed out, Thing might have a different index in a different sequence. We'd
need a new concept or class of a sequence 'slot', which will be a direct reification
of the ternary relation, eg (making up names freely on the fly..)
:FunkyPL a seq:Sequence;
seq:length 2.
_:FunkyPL_1 a seq:Slot;
seq:in_sequence :FunkyPL;
seq:index 1;
seq:item :SexMachine.
_:FunkyPL_2 a mo:PlaylistSlot;
seq:in_sequence :FunkyPL;
seq:index 2;
seq:item :GoodFoot.
My RDF writing skills are but weak but I think if seq:has_slot was defined as
an inverse property to seq:in_sequence, this could be written sort of like this:
:FunkyPL a seq:Sequence;
seq:length 2;
seq:has_slot {
seq:index 1;
seq:item :SexMachine
};
seq:has_slot {
seq:index 2;
seq:item :GoodFoot
}.
... which is almost readable.
The justification thing Kurt proposed could be attached to a sub-class
of seq:Slot, eg mo:PlaylistSlot, as long as seq:index > 1.
Going back to Yves' mention of Tracks and track_number, this
suggest that a Track plays the role of a seq:Slot for mo:Signals in the
context of the sequence defined by an album.
The reason I've gone on at such length about this is that it's by no
means an isolated problem - I started working with the chord ontology
recently and found the representation of notes and scale degrees problematic
precisely because it was not a faithful translation of the fairly straightforward
algebraic data types that one might define for such things, and indeed we
did define in our original paper on the subject (Harte, Sandler, Abdallah, Gómez,
2005; 'Symbolic Representation of Musical Chords: A Proposed Syntax for Text Annotations').
Well, I won't go on at length about that :)
Samer.
On 26 Jun 2010, at 17:44, Kurt J wrote:
> Hello,
> I'm thinking about modeling play lists in RDF and I've come across a
> mention of the mo:Playlist concept on the wiki [1]. Is there any more
> information about this somewhere?
> My thoughts are something like this:
> mo:Playlist rdfs:subClassOf rdf:List .
> :SexMachine a mo:Track .
> :GoodFoot a mo:Track .
> :FunkyPlaylist a mo:Playlist ;
> rdf:first :SexMachine ;
> rdf:rest _:FunkyPlaylist01 ;
> mo:playlist_justification :JamesBrownGraph .
> _:FunkyPlaylist01 a mo:Playlist;
> rdf:first :GoodFoot ;
> rdf:rest _:FunkyPlaylist02 .
> :JamesBrownGraph = {
> :SexMachine foaf:maker :JamesBrown.
> :GoodFoot foaf:maker :JamesBrown.
> }
> So here we just are creating a list of mo:Tracks using the rdf:List
> model. We add some detail with the mo:playlist_justification
> property. This points to a graph that contains a set of triples that
> show how the two tracks in that particular step of the playlist are
> connected. In the above example the first two track are both made by
> :JamesBrown.
> I think mo:playlist_justification is a horrible name for this property
> and perhaps this isn't the best way to model this. Hopefully my
> intent is clear enough. Any suggestions on this???
> -kurt j
> [1] - http://wiki.musicontology.com/index.php/Todo_list
> --
> You received this message because you are subscribed to the Google Groups "Music Ontology Specification Group" group.
> To post to this group, send email to music-ontology-specification-group@googlegroups.com.
> To unsubscribe from this group, send email to music-ontology-specification-group+unsubscribe@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/music-ontology-specification-group?hl=en.